기본적으로 중첩 클래스는 정적 클래스 혹은 인스턴스로 생성되기 때문에 접근 제한이 걸린다.
바깥 필드와 메소드에서 사용 제한
바깥 클래스의 인스턴스 필드와 메소드에서는 일반 중첩 클래스와 정적 중첩 클래스 둘 다 사용 가능하다.
바깥 클래스의 정적 필드와 메소드에서는 정적 중첩 클래스만 사용 가능하다.
생각해보면 당연히 정적 영역이 만들어질 때, 클래스는 객체화되지 않고 설계도 상태로만 있으므로, 클래스의 정적 필드나 정적 메소드 내부에 일반 객체가 들어갈 수 없다.
멤버 클래스에서 사용 제한
중첩 클래스에서는 바깥 클래스의 필드, 메소드 사용이 가능하다.
단, 정적 중첩 클래스에서는 바깥 클래스에 선언된 정적 필드, 정적 메소드만 사용 가능하다.
그 이유는 정적 중첩 클래스가 메모리상에 먼저 생기고, 일반 중첩 클래스는 객체화되는 타이밍에 생긴다.
로컬 클래스에서 사용 제한
로컬 클래스란 클래스의 메소드에 있는 중첩 클래스를 말한다.
로컬 클래스에서 외부 클래스의 필드는 당연히 사용 가능하다.
로컬 클래스에서 메소드의 매개변수나 로컬 변수를 사용하면 직접 표기하지 않아도 final 형태로 변한다.
메모리에서의 상주 타이밍이 다르기 때문이다.
로컬 클래스는 힙 메모리에 남아서 계속 활용이 가능하지만, 메소드의 매개변수나 로컬 변수는 메소드가 종료될 때 스택에서 할당된 메모리가 회수(pop)된다.
자바는 이 문제를 해결하기 위해 매개변수나 로컬 변수를 복사해둔다.
코드로 정리
packagecom.company.nested_class;publicclassOuterClass {publicString instanceField ="instanceField";publicstaticString staticField ="staticField";classInnerClass {String innerClassInstanceField = instanceField;String innerClassStaticField = staticField;publicvoidinnerClassMethod() {System.out.println(instanceField);System.out.println(staticField); } /** * ERROR: 일반 중첩 클래스는 내부에서 정적인 메소드 선언이 불가능하다. * * WHY: 중첩 클래스는 외부 클래스 생성 전에는 생성되지 않기 때문에 * JVM 메모리 중 메소드 영역에 상주해야 하는 static 을 사용할 수 없다. */// public static void innerClassStaticMethod() {//// } }staticclassStaticInnerClass { /** * ERROR: 정적 중첩 클래스에서는 인스턴스 변수의 이용이 불가능하다. * * WHY: 외부 클래스의 인스턴스 멤버란 것은 외부 클래스가 객체화 되어야 * 실제적인 주소를 갖는다. 그런데, static 영역은 그 주소를 갖기 전에 초기화된다. */// String staticInnerClassField = instanceField;String staticInnerClassStaticField = staticField;publicvoidstaticInnerClassMethod() {// System.out.println(instanceField);System.out.println(staticField); }publicstaticvoidstaticInnerClassStaticMethod() {// System.out.println(instanceField);System.out.println(staticField); } }voidouterMethod(finalint arg1,int arg2,String fs) {finalint var1 =1;int var2 =2;classLocalClass {voidmethod() {// 아래의 코드로 인해 사실상 모두 final 로 변경됨int result = arg1 + arg2 + var1 + var2; } }/** * ERROR: 로컬 클래스에서 사용된 메소드 파라미터 및 매개변수는 * final 효과를 가져 수정할 수 없다. * * WHY: 클래스의 메소드 내부에서 생성된 로컬 클래스의 객체는 * 메소드 실행이 끝나도 힙 메모리에 존재해서 계속 사용할 수 있다. * 반면에 메소드 블록에 존재하는 변수나 매개변수들은 메소드 실행이 끝나면, * 스택에서 제거되어 더이상 사용할 수 없게 된다. * final 로 이루어진 상수를 참조하는 것은 스택 메모리 영역을 사용하지 않아 괜찮은데, * 변수를 참조하면 해당 메모리 영역 자체가 스택에서 지워지기 때문에 안된다. */// arg2 = 10000; }}
중첩 클래스에서 바깥 클래스 참조 얻기
중첩 클래스에서 this를 사용하면 중첩 클래스의 객체를 가리킨다.
바깥 클래스 객체의 참조를 얻으려면 바깥클래스명.this를 해야 한다.
publicclassOuterClass {String field ="Outter-field";Class NestedClass {voidprintOuterField() {System.out.println(OuterClass.this.field); } }}
중첩 인터페이스
클래스의 멤버로 선언된 인터페이스를 말한다.
클래스 내부에 선언하는 이유는 해당 클래스와 관계를 맺기 때문이다.
UI 처리 시에 많이 이용한다.
중첩 인터페이스 예제 Button 클래스
publicclassButton {OnClickListener listener; // 인터페이스 타입 필드voidsetOnClickListener(OnClickListener listener) {this.listener= listener; // 매개변수의 다형성// OnClickListener인터페이스를 상속하는 객체를 받을 수 있다. }// 터치 시 구현 객체의 메소드 호출voidtouch() {listener.onClick(); }// 중첩 인터페이스interfaceOnClickListener {voidonclick(); }}