본문 바로가기
👨‍🏫Study/JAVA

[JAVA] 09 - 1 중첩 클래스와 중첩 인터페이스 소개

by 코푸는 개발자 2022. 3. 21.
728x90

 

중첩 클래스

 

- 클래스 내부에 선언하는 클래스를 말함 

- 중첩 클래스를 통해 두 클래스 멤버들이 서로 쉽게 접근할 수 있음

- 클래스 안에 클래스를 정리할 수 있어서 정리가 되어 코드의 복잡성을 줄일 수 있음

- 클래스 내부에 인터페이스를 선언하면 그것은 중첩 인터페이스임 (해당 클래스의 구현 클래스를 만들기 위해 선언)

 

중첩 클래스의 종류

 

- 멤버 클래스 : 클래스의 멤버로서 선언됨 -> 클래스나 객체가 사용 중일 때는 언제든 재사용 가능

  멤버 클래스 안에서는 인스턴스 멤버 클래스(static이 붙지 않음)와 정적 멤버 클래스(static이 붙음)로 나뉜다. 

- 로컬 클래스 : 생성자나 메소드 내부에 선언됨 -> 해당 메소드를 실행할 때만 사용 가능

- 정적 멤버 클래스 : 상위 객체가 인스터스화되지 않아도 접근 간으(내부에 정적 필도 혹은 메소드 선언이 가능)

 

바이트 코드 파일 이름

 

- 중첩 클래스도 클래스의 일종이므로 컴파일시 바이트 코드파일이 별도로 생성됨

- 멤버 클래스 : A$B.class

- 로컬 클래스 : A$1B.class 

 

 

인스턴스 멤버 클래스

 

- static 키워드 없이 중첩 선언된 클래스

- 인스턴스 멤버 클래스 안에서는 인스턴스 필드와 인스턴스 메소드만 선언이 가능하다.

- 정적 필드와 정적 메소드는 선언이 불가능하다.

 

- A 클래스 안에서 B 클래스가 인스턴스 멤버 클래스로 선언되었을 때

A 클래스 외부에서 B 객체를 생성하기 위해서는 먼저 A 객체를 생성한 후 B 객체를 생성해야한다.

A 클래스 내부에서는 일반 클래스처럼 B 객체를 생성한다.

class A {
	/** 인스턴스 멤버 클래스**/
    class B {
    	B() {} // 생성자
        int field1; // 인스턴스 필드
        // static int field2; // 정적 필드 선언할 수 없음
        void method1() {} // 인스턴스 메소드
        // static void method2() {} // 정적 메소드 선언할 수 없음
    }
}

// A 클래스 외부
A a = new A();
A.B b = a.new B();
b.field1 = 3;
b.method1();


// A 클래스 내부
class A {
    class B { }
    
    void methodA() {
    B b = new B(); // A 클래스 내부에서는 B 객체 바로 선언 가능
    b.field1 = 3; 
    b.method1();
    }
}

 

 

정적 멤버 클래스

 

- static 키워드로 중첩 선언된 클래스

- 정적 필드와 정적 메소드, 인스턴스 필드와 인스턴스 메소드 모두 선언이 가능하다.

 

- A 클래스 안에서 C 클래스가 정적 멤버 클래스로 선언되었을 때

A 클래스 외부에서 C 객체를 생성하기 위해서는 먼저 A 객체를 생성할 필요가 없다.

후 B 객체를 생성해야한다. (A.B b = a.new B();)

A 클래스 내부에서는 일반 클래스처럼 B 객체를 생성한다.

class A {
	/** 정적 멤버 클래스**/
    static class C {
    	C() {} // 생성자
        int field1; // 인스턴스 필드
        static int field2; // 정적 필드
        void method1() [] // 인스턴스 메소드
        static void method2() // 정적 메소드
    }
}

// A 클래스 외부
A.C c = new A.C();
c.field1 = 3; // 인스턴스 필드 사용
c.method1(); // 인스턴스 메소드 호출
A.C.field2 = 3; // 정적 필드 사용
A.C.method2(); // 정적 메소드 호출

 

로컬 클래스

 

- 메소드 내에서 선언되는 클래스로서 어차피 메소드 내부에서만 사용 가능하므로 접근제한자 및 static 을 붙일 수 없다

- 로컬 클래스 내부에서 필드와 메소드를 선언할 때 정적 필드와 정적 메소드는 선언할 수 없다.

 

void method() { // 메소드 내부에서 클래스 선언, 객체 생성, 사용 다 한다.
	class D {
    	D() { }
        int field1; // 인스턴스 필드
        // static int field2; // 정적 필드 선언 X
        void method1() { } // 인스턴스 메소드
        // static void method2() { } // 정적 메소드 선언 X
    }
D d = new D();
d.field1 = 3;
d.method1();
}

 

* 메소드 안에서 클래스를 선언하는 방법은 스레드 단원에서 배울 비동기 처리를 위한 스레드 객체를 생성할 때 사용함

 

중첩 클래스 예제

 

/** 바깥 클래스**/
public class A {
	A() {System.out.println("A 객체가 생성됨");} // 생성자

	/**인스턴스 멤버 클래스**/
	class B {
		B() {System.out.println("B 객체가 생성됨");}
		int field1;
		//static int field2;
		void method1() {}
		//static void method2() {}
	}
	
	/**정적 멤버 클래스**/
	static class C {
		C() {System.out.println("C 객체가 생성됨");}
		int field1;
		static int field2;
		void method1() {}
		static void method2() {}
	}

	void method() {
		/**로컬 클래스**/
		class D {
			D() {System.out.println("D 객체가 생성됨");}
			int field1;
			//static int field2;
			void method1() {}
			//static void method2() {}
		}
		
		D d = new D();
		d.field1 = 3;
		d.method1();
	}
}

 

public class Main {
	public static void main(String[] args) {
		A a = new A();
		
		//인스턴스 멤버 클래스 객체 생성
		A.B b = a.new B();
		b.field1 = 3;
		b.method1();
		
		//정적 멤버 클래스 객체 생성
		A.C c = new A.C();
		c.field1 = 3;
		c.method1();
		A.C.field2 = 3;
		A.C.method2();
		
		// 로컬 클래스 객체 생성을 위한 메소드 호출
		a.method();
	}
}

 

A 객체가 생성됨
B 객체가 생성됨
C 객체가 생성됨
D 객체가 생성됨

 

 

중첩 클래스의 접근 제한

 

- 중첩 클래스의 바깥에서 필드와 메소드에 대한 사용 제한

public class A1 {
	
	//인스턴스 필드
	B1 field1 = new B1();
	C1 field2 = new C1();
	
	// 인스턴스 메소드
	void method1() {
		B1 var1 = new B1();
		C1 var2 = new C1();
	}
	
	// 정적 필드 초기화
	// static B1 field3 = new B1();
	static C1 field4 = new C1();
	
	// 정적 메소드
	static void method2() {
		//B1 var1 = new B1();
		C1 var2 = new C1();
	}
	
	// 인스턴스 멤버 클래스 
	// 인스턴스 필드와 인스턴스 메소드 모두에서 객체 생성 가능
	// 그러나 정적 필드와 정적 메소드에서는 B 객체 생성 불가 
	class B1 {}
	
	// 정적 멤버 클래스 
	// 어디서든 객체 생성 가능
	static class C1 {}
}

 

 

- 멤버 클래스 내부에서 사용 제한

public class A2 {
	
	//인스턴스 필드와 메소드
	int field1;
	void method1() {}
	
	//정적 필드와 메소드
	static int field2;
	static void method2() {}
	
	
	class B2 {	// 인스턴스 멤버 클래스라서 모든 필드와 메소드에 접근 가능하다
		void method() {
			field1 = 10;
			method1();
			
			field2 = 10;	
			method2();
		}
	}
	
	static class C {	// 정적 멤버 클래스라서 정적 필드와 메소드만 접근이 가능하다.
		void method() { 
			//field1 = 10;
			//method1();
			
			field2 = 10;
			method2();
		}
	}
}

 

 

- 로컬 클래스에서 사용 제한

 

로컬 클래스에서는 메소드의 매개변수나 로컬 변수를 사용할 떼 Ffinal로 선언하도록 제한함

제한 하는 이유 : 로컬 스레드 객체의 경우 처럼 메소드가 종료되었는데도 계속 실행 상태일 수 있다.

final 로 선언하여 값이 변경되지 못하여 로컬 클래스 내부에서 복사해두고 사용할 수 있음

 

public class Outter { // 자바 8 이후 부터는 final 생략 가능 (알아서 final 특성을 지정)
	public void method1(final int arg) { // 메소드 내에서
		final int localVariable = 1; // 로컬 변수와 매개변수를 final로 선언
		// arg = 100; // 컴파일 에러 발생
		// localVariable = 100; // 컴파일 에러 발생
		// 에러 내용 : The final local variable cannot be assigned.
		
		class Inner { // 로컬 클래스 
			public void method() {
				int result = arg + localVariable; // 로컬 클래스 내부에서 로컬 변수와 매개변수 사용 
			}
		}
	}
}

 

- 중첩 클래스에서 바깥 클래스 참조 얻기

this.필드; // 자기 자신의 참조
this.method;
 
바깥클래스명.this.필드; // 중첩 클래스 내부에서 바깥 클래스의 객체 참조
바깥클래스명.this.method;

 

- 중첩 클래스에서 바깥 클래스 참조 얻기 예제

class Outter { // 바깥 클래스
	String field = "Outter-field"; // 바깥 클래스 필드 선언
	void method() { // 바깥 클래스 메소드 선언
		System.out.println("Outter-method");
	}

	class Nested { // 중첩 클래스
		String field = "Nested-field"; // 중첩 클래스 필드 선언
		void method() { // 중첩 클래스 메소드 선언
			System.out.println("Nested-method");
		}

		void print() { // 중첩 클래스 내부에서 print() 메소드 선언
			
			// 중첩 클래스 내부에서 중첩 클래스 자신의 필드와 메소드 사용
			System.out.println(this.field); // 그냥 this만 사용
			this.method();

			// 중첩 클래스 내부에서 바깥 클래스 필드와 메소드 사용
			System.out.println(Outter.this.field); // 바깥클래스명.this 사용
			Outter.this.method();
		}
	}
}

 

<실행 파일>

public class OutterExample {
	public static void main(String[] args) {
		Outter outter = new Outter(); // 바깥 클래스의 객체 생성
		Outter.Nested nested = outter.new Nested(); // 중첩 클래스 객체 생성
		nested.print(); // 중첩 클래스 메소드 선언
	}
}

 

중첩 인터페이스

 

-  해당 클래스와 긴밀한 관계를 맺는 구현 클래스를 만들기 위해 클래스의 멤버로서 선언된 인터페이스

- 중첩 인터페이스 : 인스턴스와 정적 멤버 모두 가능하다.

 

- 인스턴스 멤버 인터페이스는 바깥 클래스의 객체가 있어야 사용 가능

- 정적 멤버 인터페이스는 바깥 클래스의 객체 없이 바깥 클래스만으로 바로 접근 가능 

- 중첩 인터페이스 사용 예시 > 클래스 내부에 선언된 중첩 인터페이스를 구현한! 객체만 받고 싶을 때 사용

 

<Button 클래스 내에서 선언된 중첩 인터페이스>

class Button {
	
	//static으로 중첩 인터페이스로 선언(이벤트 처리 목적)
	static interface OnClickListener { 
		void onClick(); // 인터페이스 내 추상 메소드 선언
	}
	
	// 인터페이스 타입 필드
	OnClickListener listener; 
	
	// 인터페이스 타입을 매개 값으로 받는 Setter 메소드 선언
	void setOnClickListener(OnClickListener listener) { // 매개변수로 구현 객체가 들어옴
		this.listener = listener; // 구현 객체를 받아 필드에 대입
	}
	
	// 버튼 이벤트 발생 메소드
	void touch() { 
		listener.onClick(); // 인터페이스를 통해 구현 객체의 onClick() 메소드 호출
	}
}

 

<구현 클래스>

import ch09_1_inner.Button.OnClickListener;

// 구현 클래스 1 - 전화 
class CallListener implements OnClickListener {
	@Override
	public void onClick() {
		System.out.println("전화를 겁니다.");
	}
}

//구현 클래스 1 - 메시지
class MessageListener implements OnClickListener {
	@Override
	public void onClick() {
		System.out.println("메시지를 보냅니다.");
	}
}

 

<버튼 이벤트 처리 실행 클래스>

public class ButtonExample {
	public static void main(String[] args) {
		Button btn = new Button();
		
		btn.setOnClickListener(new CallListener());
		btn.touch(); // 전화를 겁니다.
		
		btn.setOnClickListener(new MessageListener());
		btn.touch(); // 메시지를 보냅니다.
	}
}

 

728x90

'👨‍🏫Study > JAVA' 카테고리의 다른 글

[JAVA] 10 - 1 예외 클래스  (0) 2022.03.21
[JAVA] 09 - 2 익명 객체  (1) 2022.03.21
[JAVA] 08 - 2 타입 변환과 다형성  (0) 2022.03.21
[JAVA] 08 - 1 인터페이스  (0) 2022.03.21
[JAVA] 07 - 3 추상 클래스  (0) 2022.03.21

댓글