item 24) 멤버 클래스는 되도록 static으로 만들라
중첩 클래스
다른 클래스 안에 정의된 클래스
자신을 감싼 바깥 클래스(선언된 클래스)에서만 쓰여야 하며 그 외 쓰임새가 있다면 톱레벨 클래스로 만들어야 한다.
종류: 정적 멤버 클래스, 멤버 클래스, 익명 클래스, 지역클래스
정적 멤버 클래스 (static member class)
다른 클래스 안에 선언되고, 선언된 바깥 클래스의 private 멤버에 접근할 수 있다.
다른 정적 멤버와 똑같은 접근 규칙 적용하므로, private으로 선언되면 바깥 클래스에서만 접근할 수 있다.
바깥 클래스와 함께 쓰일 때에만 유용한 public 도우미 클래스로 사용된다.
Opration.PLUS, Operation.MINUS와 같이 enum 타입 Operation 클래스가 Calculator 클래스의 public 정적 멤버 클래스가 되면 클라이언트는 원하는 연산을 참조할 수 있다.
private 정적 멤버 클래스: 바깥 클래스가 표현하는 객체의 한 부분을 나타낼 때 사용
멤버 클래스
비정적 멤버 클래스
바깥 클래스의 인스턴스와 암묵적으로 연결되어 인스턴스 메서드에서 정규화된 this를 사용해 바깥 클래스 인스턴스의 메서드를 호출하거나 참조를 가져올 수 있다.
정규화된 this: 클래스명.this 형태로 선언된 클래스의 이름을 명시하는 용법
바깥 클래스의 인스턴스 없이는 생성할 수 없으므로, 개념적으로 중첩 클래스와 바깥 클래스의 인스턴스가 각각 독립적으로 존재할 수 있다면 정적 멤버 클래스로 만들어야 한다.
바깥 인스턴스와 멤버 클래스 인스턴스 간 관계는 멤버 클래스가 인스턴스화될 때 확립된다.
멤버 클래스가 인스턴스화되는 경우는 다음 두가지가 있다.
1) 바깥 클래스의 인스턴스 메서드에서 멤버 클래스의 생성자를 호출할 때
2) 직접 바깥인스턴스 클래스에서 수동으로 new를 사용해 멤버 클래스의 인스턴스 만들 때
어댑터 정의할 때 자주 사용
ex) Map, Set, List같은 컬렉션 인터페이스의 구현체들은 자신의 컬렉션 뷰를 구현할 때 멤버 클래스를 사용한다.
멤버 클래스에서 바깥 인스턴스에 접근할 일이 없다면 정적 멤버 클래스로 만들 것
바깥 인스턴스로의 숨은 외부 참조를 갖게 되어 메모리 공간을 차지하며 생성 시간이 걸림
가비지 컬렉션이 바깥 인스턴스를 수거하지 못하는 메모리 누수 발생 가능
참조가 눈에 보이지 않으므로 디버깅도 어려움
예제
아래는 내부 클래스를 사용 시 참조가 있다는 점을 간과하고 코드를 짜면 발생하는 문제에 대한 예시이다. ButtonState는 Serializable 구현체이지만 Button의 내부 클래스이므로 Button에 대한 참조를 갖게 되고, Button은 Serializable이 아니게 되므로 ButtonState를 직렬화할 때 오류가 발생한다.
private 정적 멤버 클래스
바깥 클래스가 표현하는 객체의 구성요소를 나타낼 때 사용
Map 구현체의 Entry 객체는 내부 메서드(getKey, getValue, setValue)를 가지지만, Map 구현체에서는 사용하지 않는다.
Map 객체의 private 정적 멤버 클래스로 Entry 객체를 표현하면 바깥 맵으로의 참조를 갖지 않아 공간, 시간 절약 가능
공개된 클래스의 public이나 protected 멤버 클래스라면 혹시 나중 릴리즈에서 static을 붙이면 하위 호환성이 깨지므로 static 여부가 중요하다.
익명클래스
쓰이는 시점에 선언과 동시에 인스턴스를 생성하며, 코드의 어디서는 만들 수 있다.
static하지 않은 상황에서 사용될 때에만 바깥 클래스의 인스턴스 참조 가능
static한 상황에서 상수 변수 이외의 정적 멤버는 가질 수 없다.
상수 표현을 위해 초기화된 final 기본타입과 문자열 필드만 가질 수 있다.
instanceof 검사나 클래스의 이름이 필요한 작업은 수행할 수 없다.
여러 인터페이스를 구현할 수 없고, 인터페이스 구현하는 동시에 다른 클래스 상속 불가
익명 클래스가 상위 타입에서 상속한 멤버 외에는 호출 불가
익명 클래스가 짧지 않으면 가독성이 떨어진다.
작은 함수 객체나 처리 객체를 만드는 역할을 했었으나, 현재는 람다를 사용한
정적 팩토리 메서드 구현할 때 사용
지역클래스
가장 드물게 사용되는 중첩 클래스
지역변수를 선언할 수 있는 곳에서 선언 가능하며 유효 범위도 지역변수와 같다.
멤버 클래스처럼 이름이 있고 반복해서 사용 가능
static하지 않은 상황에서 사용될 때에만 바깥 인스턴스 참조 가능
정적 멤버를 가질 수 없으며 가독성을 위해 짧게 작성해야 한다.
중첩 클래스 사용법
메서드 밖에서 사용해야 하거나 메서드 안에 정의하기 길면 -> 멤버 클래스
멤버 클래스의 인스턴스 각각이 바깥 인스턴스 참조하면 -> 비정적 멤버 클래스, 참조하지 않으면 -> 정적 멤버 클래스
중첩 클래스가 한 메서드에서만 쓰이고 인스턴스 생성하는 지점이 한곳이고 타입으로 사용하기에 적합한 클래스나 인터페이스가 있으면 -> 익명클래스
Last updated