class ClassName {
class NestedClassName { // 중첩클래스
}
}
// 인터페이스도 클래스 내부에 선언 가능
class ClassName {
interface NestedInterfaceName { // 중첩 인터페이스
}
}
중첩클래스도 클래스이기 때문에 컴파일하면 .class 파일이 별도로 생성
A $ B.class
A: 바깥 클래스, B: 멤버 클래스
A $1 B.class
A: 바깥 클래스, B: 로컬 클래스
인스턴스 멤버 클래스
/**바깥 클래스**/
class A {
A() { System.out.println("A 객체가 생성됨"); }
/**인스턴스 멤버 클래스**/
public class B {
// 생성자
B() { System.out.println("B 객체가 생성됨"); }
int field1; // 인스턴스 필드
//static int field2; // 정적필드 사용 불가
void method1() { } // 인스턴스 메소드
//static void method2() { }; // 정적메소드 사용 불가
}
정적 멤버 클래스
class A {
/**정적 멤버 클래스**/
static class C {
C() { System.out.println("C 객체가 생성됨"); }
int field1;
static int field2;
void method1() { }
static void method2() { }
}
A.C c = new A.C();
c.field1 = 3; // 인스턴스
c.method1();
A.C.field2 = 3; // 정적
A.C.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();
}
void method() {
class DownloadThread extends Thread { ...}
DownloadThread thread = new DownloadThread();
thread.start();
}
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 객체가 생성됨
인스턴스 : 바깥.new 멤버(), 정적: new 바깥.멤버() !!바깥 클래스에서 인스턴스 멤버 클래스 사용할 때 제한
public class A {
//인스턴스 필드
B field1 = new B();
C field2 = new C();
//인스턴스 메소드
void method1() {
B var1 = new B();
C var2 = new C();
}
//정적 필드 초기화
//static B field3 = new B();
static C field4 = new C();
//정적 메소드
static void method2() {
//B var1 = new B();
C var2 = new C();
}
//인스턴스 멤버 클래스
class B {}
//정적 멤버 클래스
static class C {}
}
위의 코드에서 인스턴스 멤버 클래스 B는 바깥 클래스의 인스턴스 필드 field1의 초기값이나
인스턴스 메소드 method1에서 객체 생성할 수 있지만
정적 필드 field3의 초기값이나 정적 메소드 method2에서는 객체를 생성할 수 없다.
하지만 정적 멤버 클래스 C는 모든 필드의 초기값이나 모든 메소드에서 객체 생성할 수 있다.
멤버 클래스 내부에서 바깥 클래스의 필드와 메소드에 접근할 때 제한
public class A {
int field1;
void method1() { }
static int field2;
static void method2() { }
//인스턴스 멤버 클래스
class B {
void method() { // 모든 필드와 메소드에 접근 가능
field1 = 10;
method1();
field2 = 10;
method2();
}
}
//정적 멤버 클래스
static class C {
void method() {
//field1 = 10; 인스턴스 필드와 메소드 접근 불가
//method1();
field2 = 10;
method2();
}
}
}
로컬 클래스의 객체는 메소드 종료되면 없어지는 것이 일반적이지만, 로컬 스레드 객체를 사용할 때는 종료되어도 계속 실행 상태로 존재
자바는 이 문제를 해결하기 위해 컴파일 시 로컬 클래스에서 사용하는 매개 변수나 로컬 변수의 값을 내부에 복사해두고 사용한다.
그리고 매개변수나 로컬 변수가 수정되어 값이 변경되면 복사해둔 값과 달라지므로 매개변수나 로컬변수를 final로 선언할 것을 요구한다.
public void method2(int arg) {
int localVariable = 1;
//arg = 100; (x)
//localVariable = 100; (x)
class Inner {
public void method() {
int result = arg + localVariable;
}
}
}
중첩 클래스에서 this 키워드 사용하면 중첩 클래스의 객체 참조함
바깥 클래스의 객체 탐조 얻으려면 바깥 클래스.this 로 사용
public 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() {
System.out.println(this.field); // 중첩 객체 참조
this.method();
System.out.println(Outter.this.field); // 바깥 객체 참조
Outter.this.method();
}
}
}
public class Button {
OnClickListener listener; //인터페이스 타입 필드
void setOnClickListener(OnClickListener listener) { //매개변수의 다형성
this.listener = listener;
}
void touch() {
listener.onClick(); // 구현객체의 onClick() 메소드 호출
}
static interface OnClickListener { //중첩 인터페이스
void onClick();
}
}
class Child extends Parent { }
class A {
Parent field = new Child();
void method() {
Parent localVar = new Child();
}
}
// 필드 선언할 때 초기값으로 익명 자식 객체 생성해서 대입
class A {
Parent field = new Parent() {
int childField;
voidchildMethod() { }
@Override
void parentMethod() { }
}; // 하나의 실행문이므로 끝에는 세미콜론 반드시 붙이기
}
class TV implements RemoteControl { }
class A {
RemoteControl field = new TV();
void method() {
RemoteControl localVar = new TV();
}
}
class A {
RemoteControl field = new RemoteControl() {
@Override
void turnOn() { }
};
}