중첩 클래스(nested class)란 다른 클래스 안에 정의된 클래스를 말한다. 중첩 클래스는 자신을 감싼 바깥 클래스에서만 쓰여야 하며, 그 외의 쓰임새가 있다면 톱레벨 클래스로 만들어야한다. 중첩 클래스의 종류는 정적 멤버 클래스, 비정적 멤버 클래스, 익명 클래스, 지역 클래스 이렇게 네 가지다. 이 중 첫번째를 제외한 나머지는 중첩 클래스(inner class)에 해당한다. 이번에는 언제 어떤 중첩 클래스를 사용해야하고 왜 사용해야하는지 알아보자
정적 멤버 클래스는 다른 클래스 안에 선언되고, 바깥 클래스의 private 멤버에도 접근할 수 있다는 점만 제외하고는 일반 클래스와 똑같다. 흔히 바깥 클래스와 함께 쓰일 때만 유용한 public 도우미 클래스로 쓰인다. 계산기가 지원하는 연산 종류를 정의하는 열거 타입을 예로 들어서 생각해보자. Operation 열거 타입은 Calculator 클래스의 public 정적 멤버 클래스가 되어야 한다. Calculator의 클라이언트에서 Calculator.Operation.PLUS나 Calculator.Operation.MINUS와 같은 형태로 원하는 연산을 참조할 수 있다.
정적 멤버 클래스와 비정적 멤버 클래스의 차이는 단지 static이 있는지에 대한 여부 뿐이지만 의미상 차이는 의외로 꽤 크다. 비정적 멤버 클래스의 인스턴스는 바깥 클래스의 인스턴스와 암묵적으로 연결된다 그래서 비 정적 멤버 클래스의 인스턴스 메서드에서 정규화된 this를 사용해 바깥 인스턴스의 메서드를 호출하거나 바깥 인스턴스의 참조를 가져올 수 있다. 따라서 개념상 중첩 클래스의 인스턴스가 바깥 인스턴스와 독립적으로 존재할 수 있다면 정적 멤버 클래스로 만들어야 한다. 비정적 멤버 클래스는 바깥 인스턴스 없이는 생성할 수 없기 때문이다.
비정적 멤버 클래스의 인스턴스와 바깥 인스턴스 사이의 관계는 멤버 클래스가 인스턴스화될 때 확립되며 더 이상 변경할 수 없다.
비정적 멤버 클래스는 어댑터를 정의할 때 자주 쓰인다. 즉, 어떤 클래스의 인스턴스를 감싸 마치 다른 클래스의 인스턴스처럼 보이게 하는 뷰로 사용하는 것이다. 예컨대 Map 인터페이스의 구현체들은 보통 자신의 컬렉션 뷰를 구현할 때 비정적 멤버 클래스를 사용한다.
public class MySet<E> extends AbsractSet<E> {
@Override
public Iterator<E> iterator() {
return new MyIterator();
}
private class MyIterator implements Iterator<E> {
...
}
}
멤버 클래스에서 바깥 인스턴스에 접근할 일이 없다면 무조건 static을 붙여서 정적 멤버 클래스로 만드는 것이 좋다. static을 생략할 시에 바깥 인스턴스로의 숨은 외부 참조를 가지게 되고 가비지 컬렉션 등의 문제가 생길 수 있다.
익명 클래스는 바깥 클래스의 멤버가 아니다. 멤버와 달리 쓰이는 시점에 선언과 동시에 인스턴스가 만들어 진다. 코드의 어디서든지 만들 수 있고, 비정적인 문맥에서 사용될 때만 바깥 클래스의 인스턴스를 참조할 수 있다
자바 7엑서 람다를 지원하기 전에는 즉석에서 작은 함수나 객체 처리를 하기 위해 주로 사용했었지만 이제 람다에게 자리를 내어 주었다(item42)
지역 클래스는 네 가지 중첩 클래스 중에서 가장 드물게 사용된다. 지역 클래스는 지역변수를 선언할 수 있는 곳이면 실질적으로 어디서든 선언할 수 있고, 유효 범위도 지역변수와 같다.
중첩 클래스에는 네 가지가 있으며, 각각의 쓰임새가 다르다. 메서드 밖에서도 사용해야 하거나 메서드 안에 정의하기엔 너무 길다면 멤버 클래스로 만든다. 멤버 클래스의 인스턴스 각각이 바깥 인스턴스를 참조한다면 비정적으로, 그렇지 않다면 정적으로 만들자.