profile image

L o a d i n g . . .

중첩 클래스(nested class)?

클래스 내부에 선언한 클래스. 특정 클래스와 관계를 맺을 경우 중첩 클래스를 사용하면 두 클래스의 멤버들을 서로 쉽게 접근할 수 있고, 외부에는 불필요한 관계 클래스를 감춤으로써 코드의 복잡성을 줄일 수 있다.

 

 

선언 위치에 따른 분류 선언위치 설명
멤버 클래스
(클래스의 멤버로서 선언)
인스턴스 멤버클래스
(인스턴스 필드와 메소드만 선언가능)
class A {
    class B {...}
}
A 객체를 생성해야만 사용할 수 있는 B 클래스
정적멤버 클래스
(모든종류의 필드와 메소드 선언가능)
class A {
    static class B {...}
}
A 클래스로 바로 접근할 수 있는 B 클래스
로컬클래스
(생성자 또는 메소드 내부에서 선언)
주로 비동기 처리를 위한 스레드 객체를 만들 때 사용
class A {
    void method () {
        class B {...}
    }
}
method()가 실행될 때만 사용할 수 있는 B 클래스

 

중첩클래스는 컴파일 시 바이트 코드 파일이 별도로 생성된다

ex) A$B.class(멤버클래스) | A$1B.class(로컬 클래스)

 

 

package a220713;

class A {
	A(){System.out.println("A 객체가 생성됨");}
	
	/*인스턴스 멤버 클래스*/
	class B {
		B(){System.out.println("B 객체가 생성됨");}
		int field1;
		// static int field2 에러
		void method1() {}
		// static void method2(){} 에러
		int field2;
		// static int field2 에러
		void method2() {}
		// 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.method();
	}
}

참고 : A클래스 외부에서 B클래스 객체를 생성하는 일은 거의 없고 대부분 A 클래스 내부에서 생성해서 사용

 

 

 

 

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

로컬 스레드 객체를 사용할 때 메소드 실행이 종료되면 없어지는것이 일반적이나, 로컬스레드 객체는 메소드를 실행하는 스레드와 다르므로 메소드가 종료된 후에도 로컬 스레드 객체가 실행상태로 존재할 수 있다. 

이 문제 해결을 위해 자바는 컴파일 시 로컬클래스에서 사용하는 매개변수나 로컬 변수의 값을 로컬 클래스 내부에 복사해두고 사용한다. 그리고 매개변수나 로컬 변수가 수정되어 값이 변경될 시, 로컬 클래스에 복사해둔 값과 달라지므로 문제 해결을 위해 매개 변수나 로컬변수를 final로 선언할 것을 요구한다. (자바 7 이전) 

자바 8부터는 final 키워드 없이 선언된 매개변수와 로컬 변수를 사용해도 에러가 발생하지 않으나 final 선언을 하지않아도 값이 수정될 수 없도록 final 특성을 부여한다.

public class Outer {
	public void method1(final int arg) {
		final int localVariable = 1;
//		arg = 100;
//		localVariable = 100;
		class Inner{
			public void method() {
				int result = arg + localVariable;
			}
		}
	}
	
	//자바 8 이후
	public void method2(int arg) {
		int localVariable = 1;
//		arg = 100;
//		localVariable = 100;
		class Inner{
			public void method() {
				int result = arg + localVariable;
			}
		}
	}
}

 

 

 

중첩 클래스의 this

클래스 내부에서 this는 객체 자신의 참조. 

중첩 클래스에서 this 키워드를 사용하면 바깥 클래스의 객체 참조가 아닌 중첩 클래스의 객체 참조가 된다.

 

중첩 클래스의 내부에서 바깥 클래스의 객체 참조를 얻으려면 바깥 클래스 이름을 this 앞에 붙여주면 된다

 

public class OuterNested {
	String field = "Outer-field";
	void method() {
		System.out.println("Outer-method");
	}
	
	class Nested {
		String field = "Nested-field";
		void method() {
			System.out.println("Nested-method");
		}
		void print() {
			System.out.println(this.field);
			this.method();
			System.out.println(OuterNested.this.field);
			OuterNested.this.method();
		}
	}
}
public class OuterEx {
	public static void main(String[] args) {
		OuterNested on = new OuterNested();
		OuterNested.Nested n = on.new Nested();
		n.print();
	}
}

 

 

 

 

 

 

 

 

 

 

중첩 인터페이스?(nested interface)?

인터페이스 또한 클래스 내부에 선언하여 해당 클래스와 긴밀한 관계를 맺는 구현 클래스를 만든다

주로 정적 멤버 인터페이스를 많이 사용하는데, UI프로그래밍에서 이벤트를 처리할 목적으로 많이 처리됩니다.

ex) button 클릭시 button 내부에 선언된 중첩 인터페이스를 구현한 객체만 받을 때

 

public class Button {
	//인터페이스 타입 필드
	OnClickListener listener;
	
	//매개 변수의 다형성
	void setOnClickListener(OnClickListener listener) {
		this.listener = listener;
	}
	
	//구현 객체의 onClick()메소드 호출
	void touch() {
		listener.onClick();
	}
	
	//중첩인터페이스
	static interface OnClickListener {
		void onClick();
	}
}

 

 

 

 

Button 중첩 인터페이스인 OnClickListener을 구현한 클래스 생성하기

public class CallListener implements Button.OnClickListener{

	@Override
	public void onClick() {
		System.out.println("전화를 겁니다");
	}
}

public class MessageListener implements Button.OnClickListener{

	@Override
	public void onClick() {
		System.out.println("메시지를 보냅니다");
	}
}

 

 

버튼을 클릭했을때 두 방법으로 이벤트를 처리하기

public class ButtonEx {
	public static void main(String[] args) {
		Button button = new Button();
		
		button.setOnClickListener(new CallListener());
		button.touch();
		
		button.setOnClickListener(new MessageListener());
		button.touch();
	}
}

Button 객체의 setOnClickListener() 메소드에 어떤 구현 객체를 생성해서 셋팅하느냐에 따라 Button touch()메소드의 실행결과가 달라진다

 

 

 

 

 

 

 

 

 

 

 

 

 

 

참고 : 

- 혼자 공부하는 자바(신용권), 한빛미디어

반응형

'개발 > JAVA' 카테고리의 다른 글

[Java] 예외처리  (0) 2022.07.18
[Java] 익명 객체  (0) 2022.07.13
[Java] 인터페이스(Interface)  (0) 2022.07.12
[Java] 추상(abstract) 클래스  (0) 2022.07.12
[Java] 타입 변환과 다형성  (0) 2022.07.11
복사했습니다!