중첩 인터페이스는 클래스의 멤버로 선언된 인터페이스를 말한다.
인터페이스를 클래스 내부에 선언하는 이유는 해당 클래스와 긴밀한 관계를 맺는 구현 클래스를 만들기 위해서이다.
class A {
interface { //중첩 인터페이스
void method();
}
}
익명 객체는 이름이 없는 객체를 말한다.
부모 타입으로 필드나 변수를 선언하고 자식 객체를 초기값으로 대입할 경우를 생각해보자.
아래와 같이
new
연산자를 이용해서 자식 객체를 생성한 후,class Child extends Parent {} //자식 클래스 선언
class A {
Parent field = new Child(); //필드에 자식 객체를 대입
void method() {
Parent localVar = new Child(); //로컬 변수에 자식 객체를 대입
}
}
그러나 자식 클래스가 재사용되지 않고, 오로지 해당 필드와 변수의 초기값으로만 사용된다면
익명 자식 객체를 생성해서 초기값으로 대입하는 것이 좋은 방법이다.
부모클래스 [필드|변수] = new 부모클래스(매개값, ...) {
//필드
//메소드
}
class A {
Parent field = new Parent() { //A 클래스의 필드 선언
int childField;
void childMethod() { }
@Override //Parent의 메소드를 오버라이딩
void parentMethod() { }
}
}
class A {
void method() {
Parent field = new Parent() { //로컬 변수 선언
int childField;
void childMethod() { }
@Override //Parent의 메소드를 오버라이딩
void parentMethod() { }
}
}
}
이번에는 인터페이스 타입으로 필드나 변수를 선언하고, 구현 객체를 초기값으로 대입하는 경우를 생각해보자.
아래와 같이
new
연산자를 이용해서 구현 객체를 생성한 후,class TV implements RemoteControl { }
class A {
RemoteControl field = new TV(); //필드에 구현 객체를 대입
void method() {
RemoteControl localVar = new TV(); //로컬 변수에 구현 객체를 대입
}
}
구현 클래스가 재사용되지 않고, 오로지 해당 필드와 변수의 초기값으로만 사용하는 경우라면, 역시 익명 구현 객체를 초기값으로 대입하는 것이 좋다.
인터페이스 [필드|변수] = new 인터페이스() {
//인터페이스에 선언된 추상 메소드의 실체 메소드 선언
//필드
//메소드
}
추가적으로 필드와 메소드를 선언할 수는 있지만, 실체 메소드에서만 사용이 가능하고 외부에서는 사용하지 못한다.
class A {
RemoteControl field = new RemoteControl() {
@Override
void turnOn() { } //RemoteControl 인터페이스의 추상 메소드에 대한 실체 메소드
}
}
void method() {
RemoteControl localVar = new RemoteControl() {
@Override
void turnOn() { } //RemoteControl 인터페이스의 추상 메소드에 대한 실체 메소드
}
}
익명 객체 내부에서는 바깥 클래스의 필드나 메소드는 제한 없이 사용할 수 있지만,
매개 변수나 로컬 변수를 익명 객체에 사용할 때 문제가 발생한다!
메소드 내에서 생성된 익명 객체는 메소드 실행이 끝나도 힙 메모리에 존재해서 계속 사용할 수 있는데, 매개 변수나 로컬 변수는 메소드 실행이 끝나면 스택 메모리에서 사라지기 때문에 익명 객체에서 사용할 수 없게 되기 때문이다.
이 문제를 해결하기 위해서는 익명 객체 내부에서 메소드의 매개 변수나 로컬 변수를 사용할 경우 final
특성을 갖도록 해야 한다.