KDT/Java

240311 Java - 내부 클래스2

001cloudid 2024. 3. 11. 12:50
728x90

내부 클래스

  • 클래스 내부에 선언된 또 다른 클래스
  • 외부 클래스와 밀접한 관련이 있고 다른 클래스와는 협력할 일이 없는 경우에 내부 클래스로 선언해서 사용
  • 내부 클래스는 중첩 클래스라고도 함
  • 내부 클래스에서 외부 클래스의 멤버에 손쉽게 접근할 수 있음
  • 서로 관련 있는 클래스를 논리적으로 묶어서 표현함으로써, 코드의 캡슐화를 증가시킴
  • 외부에서는 내부 클래스를 접근할 수 없으므로 코드의 복잡서을 줄일 수 있음

내부 클래스 종류

  • 인스턴스 내부 클래스
    static 키워드를 가지지 않는 클래스
    외부 클래스의 인스턴스 변수나 인스턴스 메소드에 사용될 목적으로 선언
  • 정적 내부 클래스
    static 키워드를 가지는 클래스
    외부 클래스의 클래스(정적) 변수나 인스턴스 메소드에 사용될 목적으로 선언
  • 지역 내부 클래스
    외부 클래스의 메소드나 초기화 블록에 선언된 클래스를 의미
  • 익명 내부 클래스
    이름을 가지지 않는 클래스
    생성자를 선언할 수 없고 하나의 클래스나 인터페이스를 상속받거나 구현할 수 있음
    클래스 선언과 동시에 객체를 생성하고 하나의 객체만 생성하는 클래스

 

인스턴스  내부 클래스

package test16;

class OutClass{ //외부 클래스
	
	private int num = 10; //외부 클래스 private 변수
	private static int sNum = 20; //외부 클래스 정적 변수
	
	private InClass inClass; //내부 클래스 자료형 변수를 먼저 생성
	
	//외부 클래스 디폴트 생성자. 외부 클래스가 생성된 후에 내부 클래스 생성 가능
	public OutClass() {
		inClass = new InClass();
	}
	
	class InClass{ //인스턴스 내부 클래스
		int inNum = 100; //내부 클래스의 인스턴스 변수
		//static int sInNum = 200; //인스턴스 내부 클래스에 정적 변수 선언 불가능
		
		void inTest() {
			System.out.println("OutClass num = " + num + "(외부 클래스의 인스턴스 변수)");
			System.out.println("OutClass sNum = " + sNum + "(외부 클래스의 정적 변수)");
			System.out.println("InClass innum = " + inNum + "(내부 클래스의 인스턴스 변수");
		}
	}
		
		//static void sTest(){} //정적 메소드 정의 불가능
			
		public void usingClass() { //외부 일반 메소드 : 외부 클래스에서 내부 클래스의 메소드를 호출해서 사용
			inClass.inTest(); //내부 클래스 메소드 호출
		}
}

public class InnerTest2 {

	public static void main(String[] args) {
		
		//외부 클래스 생성 후 내부 클래스를 생성
		//외부 클래스를 먼저 생성하지 않고 인스턴스 내부 클래스를 사용할 수 없음
		//외부 클래스의 객체 생성 시 내부 클래스도 동시에 객체 생성
		OutClass outClass = new OutClass();
		outClass.usingClass(); //내부 클래스 기능 호출
		
	}

}

 

package test16;

class OutClass{ //외부 클래스
	
	private int num = 10; //외부 클래스 private 변수
	private static int sNum = 20; //외부 클래스 정적 변수
	
	private InClass inClass; //내부 클래스 자료형 변수를 먼저 생성
	
	//외부 클래스 디폴트 생성자. 외부 클래스가 생성된 후에 내부 클래스 생성 가능
	public OutClass() {
		inClass = new InClass();
	}
	
	//내부 클래스 작성 : private 접근 제어자를 지정하게 되면 다른 클래스에서 접근 할 수 없음
	//private class InClass{
	class InClass{ //인스턴스 내부 클래스
		int inNum = 100; //내부 클래스의 인스턴스 변수
		//static int sInNum = 200; //인스턴스 내부 클래스에 정적 변수 선언 불가능
		
		void inTest() {
			System.out.println("OutClass num = " + num + "(외부 클래스의 인스턴스 변수)");
			System.out.println("OutClass sNum = " + sNum + "(외부 클래스의 정적 변수)");
			System.out.println("InClass innum = " + inNum + "(내부 클래스의 인스턴스 변수");
		}
	}
		
		//static void sTest(){} //정적 메소드 정의 불가능
			
		public void usingClass() { //외부 일반 메소드 : 외부 클래스에서 내부 클래스의 메소드를 호출해서 사용
			inClass.inTest(); //내부 클래스 메소드 호출
		}
}

public class InnerTest2 {

	public static void main(String[] args) {
		
		//외부 클래스 생성 후 내부 클래스를 생성
		//외부 클래스를 먼저 생성하지 않고 인스턴스 내부 클래스를 사용할 수 없음
		//외부 클래스의 객체 생성 시 내부 클래스도 동시에 객체 생성
		OutClass outClass = new OutClass();
		outClass.usingClass(); //내부 클래스 기능 호출
		System.out.println("================");
		//private으로 지정하지 않은 내부 클래스는 다른 클래스에서 인스턴스 내부 클래스 생성이 가능
		//외부 클래스 생성 후 내부 클래스 생성
		OutClass outClass1 = new OutClass();
		outClass1.usingClass();
		//내부 클래스의 멤버에 접근
		//외부클래스.내부클래스명 객체명 = 외부클래스객체명.new 내부클래스명
		OutClass.InClass inClass = outClass1.new InClass();
		inClass.inTest();
		
		
	}

}

 

정적 내부 클래스

package test16;

class OutClass1{
	
	private int num = 10;
	private static int sNum = 20;
	
	private InStaticClass inClass;
	
	public OutClass1() {
		inClass = new InStaticClass();
	}
	
	static class InStaticClass{ //정적 내부 클래스
		int inNum = 100; //정적 내부 클래스의 인스턴스 변수
		static int sInNum = 200; //정적 내부 클래스의 정적 변수
		
		//정적 내부 클래스의 일반 메소드, 내부 인스턴스 메소드
		void inTest() {
			System.out.println("InStaticClass inNum = " + inNum + "(내부 클래스의 인스턴스 변수 사용)");
			System.out.println("InstaticClass sInNum = " + sInNum + "(내부 클래스의 정적 변수 사용)");
			//System.out.println("OutClass num = " + num +"(외부 클래스의 변수 사용)"); //외부 클래스의 객체가 생성되어 멤버 변수를 사용할 수 있어야 접근이 가능
			System.out.println("OutClass sNum = " + sNum + "(외부 클래스의 정적 변수 사용)");
		}
		
		//정적 내부 클래스의 정적 메소드, 내부 정적 메소드. static 변수만 사용 가능
		static void sTest() {
			//num += 10; //외부 클래스와 외부 클래스의 인스턴스 변수는 사용할 수 없음
			//InNum += 10;
			//System.out.println("InStaticClass inNum = " + inNum + "(내부 클래스의 인스턴스 변수 사용");
			System.out.println("InstaticClass sInNum = " + sInNum + "(내부 클래스의 정적 변수 사용)");
			//System.out.println("OutClass num = " + num +"(외부 클래스의 변수 사용)");
			System.out.println("OutClass sNum = " + sNum + "(외부 클래스의 정적 변수 사용)");
		}
	}	
}

public class InnerTest3 {

	public static void main(String[] args) {
		
		//정적 내부 클래스는 외부 클래스를 따로 생성하지 않고 정적 내부 클래스 생성 가능
		//외부클래스명.내부클래스명 객체명 = new 외부클래스명.내부클래스명();
		OutClass1.InStaticClass sInClass = new OutClass1.InStaticClass();
		sInClass.inTest();
		//정적 내부 클래스 내의 정적 메소드를 호출해서 사용
		OutClass1.InStaticClass.sTest();
	}

}

 

지역 내부 클래스

package test16;

class OutClass2{
	
	private int num = 10;
	private static int sNum = 20;
	
	//지역 내부 클래스(local inner class)
	public void outClassMethod(){
		
		class LocalClass{
			
			int inNum = 100;
			static int sInNum = 200;
		
			//내부 인스턴스 메소드
			void inTest() {
				System.out.println("LocalClass inNum = " + inNum + "(내부 클래스의 인스턴스 변수 사용)");
				System.out.println("LocalClass sInNum = " + sInNum + "(내부 클래스의 정적 변수 사용)");
				System.out.println("OutClass2 num = " + num +"(외부 클래스의 변수 사용)"); //외부 클래스의 객체가 생성되어 멤버 변수를 사용할 수 있어야 접근이 가능
				System.out.println("OutClass2 sNum = " + sNum + "(외부 클래스의 정적 변수 사용)");
			}
			static void sTest() {
				//System.out.println("InStaticClass inNum = " + inNum + "(내부 클래스의 인스턴스 변수 사용");
				System.out.println("LocalClass sInNum = " + sInNum + "(내부 클래스의 정적 변수 사용)");
				//System.out.println("OutClass2 num = " + num +"(외부 클래스의 변수 사용)");
				System.out.println("OutClass2 sNum = " + sNum + "(외부 클래스의 정적 변수 사용)");
			}
		}
	
		//메소드 내부에서 객체 생성
		LocalClass lc = new LocalClass();
		lc.inTest();
	
	}
}	

public class InnerTest4 {

	public static void main(String[] args) {
		
		//외부 클래스 객체 생성
		OutClass2 outClass = new OutClass2();
		outClass.outClassMethod(); 

	}
}

 

익명 내부 클래스

  • 다른 클래스와는 달리 이름을 가지지 않는 클래스
  • 클래스의 선언과 동시에 객체를 생성하므로 하나의 객체만을 생성하는 일회용 클래스
  • 생성자를 선언할 수 없고 하나의 클래스나 인터페이스를 상속받거나 구현할 수 있음
  • 제한적인 용도에 사용되고 구현해야 하는 메소드가 매우 적은 클래스를 구현할 때 사용
package test16;

class Some{
	
	private int a = 3;

	public int getA() {
		return a;
	}

	public void setA(int a) {
		this.a = a;
	}
	
	
}

public class InnerTest5 {

	public static void main(String[] args) {
		
		Some s1 = new Some();
		System.out.println("s1.getA() = " + s1.getA());
		
		s1.setA(10);
		System.out.println("s1.getA() = " + s1.getA());
		
		System.out.println("====================");
		
		//익명 객체 구현
		//기존의 클래스를 이용하여 멤버 변수와 멤버 메소드의 기능을 새로 구현
		//Some 클래스가 아닌 익명 클래스
		//블록의 맨 마지막에 ';'을 반드시 작성 요
		Some s2 = new Some() {

			//오버라이드로 기능 새로 구현
			private int a = 100;
			
			@Override
			public int getA() {
				return a;
			}

			@Override
			public void setA(int a) {
				this.a = a;
			}
			
		};//익명 객체 구현 시 마지막에 ';'을 반드시 작성 
		System.out.println("s2.getA() = " + s2.getA());
		s2.setA(200);
		System.out.println("s2.setA() = " + s2.getA());

	}

}

 

package test16;

//인터페이스 구현
interface Animal{
	
	//인터페이스 내에 구현할 추상 메소드만 정의
	abstract void what();
}

//인터페이스는 구현 클래스가 반드시 만들어져 있어야함
class Ani implements Animal{

	@Override
	public void what() {
		
		System.out.println("Animal 인터페이스의 what() 메소드를 구현해서 호출");
		
	}
	
}

public class InnerTest6 {

	public static void main(String[] args) {
		
		/*
		//인터페이스를 구현한 클래스 객체로 생성해서 메소드 오버라이딩 사용
		Ani a = new Ani();
		a.what();
		
		//인터페이스 변수 = 구현 클래스의 객체를 생성해서 대입
		//인터페이스에 정의된 추상 메소드와 동일한 메소드를 구현한 클래스로 객체를 생성해서 오보라이드 (구현)된 메소드 호출해서 사용
		Animal a1 = new Ani();
		a1.what();
		*/
	}

}

 

package test16;

//인터페이스 구현
interface Animal{
	
	//인터페이스 내에 구현할 추상 메소드만 정의
	abstract void what();
}

/*
//인터페이스는 구현 클래스가 반드시 만들어져 있어야함
class Ani implements Animal{

	@Override
	public void what() {
		
		System.out.println("Animal 인터페이스의 what() 메소드를 구현해서 호출");
		
	}
}
*/	
public class InnerTest6 {

	public static void main(String[] args) {
		
		/*
		//인터페이스를 구현한 클래스 객체로 생성해서 메소드 오버라이딩 사용
		Ani a = new Ani();
		a.what();
		
		//인터페이스 변수 = 구현 클래스의 객체를 생성해서 대입
		//인터페이스에 정의된 추상 메소드와 동일한 메소드를 구현한 클래스로 객체를 생성해서 오보라이드 (구현)된 메소드 호출해서 사용
		Animal a1 = new Ani();
		a1.what();
		*/
		
		//인터페이스와 추상 클래스는 객체를 생성할 수 없음
		//인터페이스로 객체를 생성
		//익명 클래스 => 익명 구현 객체
		Animal animal = new Animal() {

			@Override
			public void what() {
				
				System.out.println("Animal 인터페이스 what() 메소드를 구현하여 호출");
				System.out.println("Animal 인터페이스 구현");
				System.out.println("dog");
				
			}
		};
		
		animal.what();
		
		
	}

}

 

package test16;

//인터페이스 구현
interface Animal{
	
	//인터페이스 내에 구현할 추상 메소드만 정의
	abstract void what();
}

/*
//인터페이스는 구현 클래스가 반드시 만들어져 있어야함
class Ani implements Animal{

	@Override
	public void what() {
		
		System.out.println("Animal 인터페이스의 what() 메소드를 구현해서 호출");
		
	}
}
*/

//추상 클래스 구현
abstract class AnimalClass{
	//추상 메소드를 한 개라도 가지고 있는 클래스는 추상 클래스가 됨
	abstract void what1(); 
}

public class InnerTest6 {

	public static void main(String[] args) {
		
		/*
		//인터페이스를 구현한 클래스 객체로 생성해서 메소드 오버라이딩 사용
		Ani a = new Ani();
		a.what();
		
		//인터페이스 변수 = 구현 클래스의 객체를 생성해서 대입
		//인터페이스에 정의된 추상 메소드와 동일한 메소드를 구현한 클래스로 객체를 생성해서 오보라이드 (구현)된 메소드 호출해서 사용
		Animal a1 = new Ani();
		a1.what();
		*/
		
		//인터페이스와 추상 클래스는 객체를 생성할 수 없음
		//인터페이스로 객체를 생성
		//익명 클래스 => 익명 구현 객체
		Animal animal = new Animal() {

			@Override
			public void what() {
				
				System.out.println("Animal 인터페이스 what() 메소드를 구현하여 호출");
				System.out.println("Animal 인터페이스 구현");
				System.out.println("dog");
				
			}
		};
		
		animal.what();
		
		System.out.println("============================");
		
		//추상 클래스는 객체를 생성할 수 없음
		//익명 클래스는 익명 구현 객체
		//추상 클래스로 객체를 생성
		AnimalClass ac = new AnimalClass() {
			
			@Override
			void what1() {
				
				System.out.println("AnimalClass 추상 클래스 what1() 메소드를 구현하여 호출");
				System.out.println("AnimalClass 추상 클래스 구현");
				System.out.println("cat");
				
			}
		};
		
		ac.what1();
		
	}

}

 

내부클래스 정리

내부 클래스의 종류 => 선언된 위치에 따라 구분

1. 인스턴스 내부 클래스(Instance inner class) => InnerTest2.java

  • 내부 클래스명을 지정할 때 아무 예약어(지시어)도 붙어 있지 않은 클래스
  • 외부 클래스 영역에 선언한 클래스 중 static 키워드를 가지지 않은 클래스    
  • 주로 외부 클래스의 인스턴스 변수나 인스턴스 메소드에 사용될 목적으로 선언
  • 외부 클래스 내부에서만 생성하여 사용하는 객체를 선언할 때 사용
  • 어떤 클래스 내에 여러 변수가 있고 이를 변수 중 일부를 모아 클래스로 표현할 수 있음
  • 내부 클래스를 감싸고 있는 외부 클래스에서만 사용하기 위해서 사용
  • 다른 클래스에서 private이 아닌 내부 클래스를 생성할 수 있음
  • 외부 클래스의 내부에서만 사용할 목적이라면 내부 클래스를 private으로 선언
  • 외부 클래스 생성해야만 내부 클래스를 생성할 수 있음
  • 외부 클래스를 먼저 생성하지 않고 인스턴스 내부 클래스를 사용할 수 없음

 

2. 정적 내부 클래스(Static inner class) => InnerTest3.java

  • 외부 클래스 영역에 선언된 내부 클래스 중에서 static 키워드를 가진 클래스
  • 주로 외부 클래스(outer class)의 클래스(정적) 변수나 인스턴스 메소드에서 사용될 목적으로 선언
  • 내부 클래스가 외부 클래스 생성과 무관하게 사용할 수 있어야하고 정적 변수도 사용할 수 있도록 하는 것
  • 외부 클래스를 먼저 생성하지 않고 인스턴스 내부 클래스를 생성하고 사용할 수 있음


3. 지역 내부 클래스(Local inner class) => InnerTest4.java

  • 외부 클래스의 메소드나 초기화 블록에 선언된 클래스를 의미
  • 지역 내부 클래스는 선언된 메소드 블록 내에서만 사용할 수 있음  
  • 지역 변수처럼 메소드 내부에 클래스를 정의하여 사용하는 것
  • 메소드나 초기화 블록의 내부에서 지역변수처럼 다루어지고 선언된 영역 내부에서 사용할 수 있는 클래스
  • 객체를 메소드 블록 내에서 생성

 

4. 익명 내부 클래스(Anonymous inner class) => InnerTest5.java, InnerTest6.java

  • 다른 내부 클래스와 달리 이름을 가지지 않는 클래스를 의미
  • 클래스 서언과 동시에 객체를 생성하므로 하나의 객체만으로 생성하는 일회용 클래스
  • 생성자를 선언할 수 없고 하나의 클래스나 인터페이스를 상속받거나 구현할 수 있음
  • 매우 제한적인 용도에 사용되고 구현해야 하는 메소드가 매우 적은 클래스를 구현 할 때 사용
728x90