Search

Item 24. 멤버 클래스는 되도록 static으로 만들어라

생성일
2023/07/25 05:50
챕터
4장 - 클래스와 인터페이스

중첩 클래스

중첩 클래스(nested class)는 다른 클래스 안에 정의된 클래스를 의미하는데, 중첩 클래스는 자신을 감싼 바깥 클래스에서만 사용되어야 하며 그외에 사용되는 곳이 있다면 톱레벨 클래스로 만들어야한다.
중첩 클래스에는 정적 멤버 클래스, 비정적 멤버 클래스, 익명 클래스, 지역 클래스가 있다.

정적 멤버 클래스

정적 멤버 클래스는 바깥 클래스의 private 필드에 접근할 수 있다는 점만 제외하고 일반 클래스와 같다.
접근 제한자 규칙도 다른 정적 멤버와 동일한 규칙을 적용받는다.
private 정적 멤버 클래스는 바깥 클래스의 구성요소를 나타낼 때 주로 사용된다.

비정적 멤버 클래스

비정적 멤버 클래스의 인스턴스는 바깥 클래스의 인스턴스와 암묵적으로 연결되기 때문에, 비정적 멤버 클래스의 인스턴스 메서드에서 정규화된 this(바깥클래스명.this)를 사용해 바깥 인스턴스의 메서드를 호출하거나 참조할 수 있다.
이러한 이유로 중첩 클래스의 인스턴스가 개념상으로 바깥 인스턴스와 독립적으로 존재할 수 있다면, 중첩 클래스를 정적 멤버 클래스로 선언해야한다.
비정적 멤버 클래스는 어떤 클래스의 인스턴스를 감싸 다른 클래스의 인스턴스처럼 보이게하는 뷰로 사용하는 방식인 어댑터를 정의할 때 자주 쓰인다. 예시를 들면, Map 인터페이스의 구현체들은 자신의 컬렉션 뷰를 구현할 때 비정적 멤버 클래스를 사용한다.
public class MySet<E> extends AbstractSet<E> { ... @Override public Iterator<E> iterator() { return MyIterator(); } private class MyIterator implements Iterator<E> { ... } }
Java
복사

멤버 클래스에서 바깥 인스턴스에 접근할 일이 없다면 무조건 static을 붙여서 정적 멤버 클래스로 사용하자

static을 생략하면 바깥 인스턴스로의 숨은 외부 참조를 가져, 시간과 공간이 소비되고 가비지 컬렉션이 바깥 클래스의 인스턴스를 수거하지 못해 메모리 누수가 발생할 수 있다.
이런 참조는 암묵적으로 연결되기 때문에, 눈에 잘 보이지 않아 문제의 원인을 찾기 어렵다.

익명 클래스

익명 클래스를 사용하면 쓰이는 시점에 선언과 동시에 인스턴스가 만들어지고 사용 후 소멸되기 때문에, 바깥 클래스의 멤버가 아니다.
오직 비정적 문맥에서 사용될 때만 바깥 클래스의 인스턴스를 참조할 수 있다. 정적 문맥에서도 상수 변수 이외의 정적 멤버는 가질 수 없다.
익명 클래스는 선언한 지점에서만 인스턴스를 만들 수 있고, instanceof 검사를 할 수 없으며, 여러 인터페이스를 구현할 수 없고, 인터페이스를 구현하면서 다른 클래스를 상속하는 것도 안된다.
Java에서는 람다 전에 작은 함수 객체나 처리 객체를 만드는데 주로 사용했으나, 람다 적용 이후로는 그런 부분은 람다를 적용해 사용하며 익명 클래스는 정적 팩터리 메서드를 구현할 때나 사용된다.

지역 클래스

지역 변수를 선언할 수 있는 곳에 클래스를 선언하여, 멤버 클래스처럼 이름이 있고 반복해서 사용할 수 있지만 익명 클래스처럼 비정적 문맥에서 사용될 때만 바깥 인스턴스를 참조할 수 있고 정적 멤버를 가질 수 없다.
지역 클래스는 가독성을 위해 짧게 작성해야하고, 네 가지 중첩 클래스 중에 가장 드물게 사용된다.