중첩 클래스(nested class)란 다른 클래스 안에 정의된 클래스를 지칭하며 다음과 같이 네 가지 종류가 있습니다.
- 정적 멤버 클래스
- 비정적 멤버 클래스
- 익명 클래스
- 지역 클래스
1. 정적 멤버 클래스
- 정적 멤버 클래스는 바깥 클래스의 private 멤버에도 직접 접근이 가능하며 이외에는 일반 클래스와 같음
- 정의되어 있는 scope 범위가 클래스
- 바깥 클래스와 함께 쓰일 때만 유용한 public 도우미 클래스
- 바깥 클래스와 독립적이기 때문에 바깥 클래스 인스턴스 필요하지 않음
이전에 언급한 대로 OuterClass를 통해 함께 사용될 때 유용하며, 아래의 코드처럼 계산기 내 연산을 담당하는 enum 클래스를 선언하는 것이 대표적인 예시입니다.
- 계산기의 연산 작업을 수행하는 Operation 클래스는 계산기와 연관이 있지만 계산기에서 제공하는 필드 혹은 메서드를 직접 사용하지 않음
- 바깥 클래스인 Calculator와 독립적이기 때문에 바깥 클래스 인스턴스가 필요하지 않음
2. 비정적 멤버 클래스
- 비정적 멤버 클래스의 인스턴스는 바깥 클래스 인스턴스와 암묵적으로 연결되어 있음
- 비정적 멤버 클래스의 인스턴스 메서드에서 정규화된 this(클래스명.this 형태로 바깥 클래스의 이름을 명시하는 용법)를 사용해 바깥 인스턴스의 메서드를 호출하거나 바깥 인스턴스의 참조를 가져올 수 있음
- 멤버 클래스에서 바깥 인스턴스를 참조할 필요가 없다면 정적 멤버 클래스로 만드는 것을 권장
- 바깥 인스턴스로의 숨은 외부 참조가 생겨 참조를 저장할 때 시간과 메모리가 소비
- GC가 바깥 클래스의 인스턴스를 수거하지 못해 메모리 누수 발생 가능
- 참조가 가시적으로 보이지 않기 때문에 문제의 원인을 파악하는 것이 쉽지 않고 때로는 심각한 상황 초래 가능
- 비정적 멤버 클래스와 바깥 인스턴스 사이의 관계는 멤버 클래스가 인스턴스화될 때 확립되며 추후 변경 불가능
- 어댑터를 정의할 때 자주 쓰이는 중첩 클래스
- 어떤 클래스의 인스턴스를 감싸 마치 다른 클래스의 인스턴스처럼 보이게 하는 view로 사용
- Map 인터페이스의 구현체는 자신의 컬렉션 뷰를 구현할 때 비정적 멤버 클래스 사용
- 비슷하게 Set과 List 같은 다른 컬렉션 인터페이스 구현체들도 자신의 iterator를 구현할 때 비정적 멤버 클래스 주로 사용
어댑터 패턴
- 기존에 정의한 코드가 클라이언트에서 사용하는 인터페이스와 호환이 되지 않을 때 해당 인터페이스와 호환이 될 수 있도록 적합하게 변형해 주는 디자인 패턴
- 클라이언트가 사용하는 인터페이스를 따르지 않는 기존 코드를 재사용할 수 있게 해줌
- 호환성이 없는 인터페이스를 갖는 두 클래스를 함께 동작할 수 있게끔 하는 데 사용
- 주로 라이브러리나 프레임워크의 재사용성을 높이기 위해 쓰임
다음은 어댑터 패턴의 간단한 예시입니다.
비정적 멤버 클래스를 사용한 어댑터 패턴은 아래와 같습니다.
3. 익명 클래스
- 익명 클래스는 바깥 클래스의 멤버가 아님
- 멤버와 달리 쓰이는 시점과 동시에 인스턴스가 생성됨
- 비정적인 문맥에서 사용될 때만 바깥 클래스의 인스턴스를 참조 가능
- 정적 팩토리 메서드를 구현할 때 익명 클래스 사용
- 자바에서 람다를 사용하기 전에 주로 사용되었으며 람다가 지원되는 자바 8+ 버전부터는 거의 사용되지 않음
- 람다 표현식은 단일 추상 메서드(SAM, Single Abstract Method)를 가진 인터페이스의 구현에 사용
- 메서드가 많은 경우에는 해당 인터페이스가 람다로 적합하지 않을 수 있음
3.1 익명 클래스 제약 사항
- 선언한 지점에서만 인스턴스를 생성할 수 있으며 instanceof 검사나 클래스명이 필요한 작업은 수행 불가능
- 익명 클래스를 사용하는 클라이언트는 해당 익명 클래스의 상위 타입에서 상속한 멤버 외에는 호출 불가능
- 여러 인터페이스를 구현할 수 없고 인터페이스를 구현하는 동시에 다른 클래스를 상속할 수도 없음
- 익명 클래스는 표현식이 중간에 등장하기 때문에 짧지 않으며 가독성이 떨어짐
익명 클래스 예시
Java 8+ 이후 람다 적용
4. 지역 클래스
- 가장 드물게 사용됨
- 지역 변수를 선언하는 곳이면 어디든 지역 클래스를 정의해서 사용 가능하며 scope 범위도 지역 변수와 같음
- 멤버 클래스처럼 이름이 존재하며 반복해서 사용 가능
- 익명 클래스처럼 비정적 문맥에서 사용될 때만 바깥 인스턴스를 참조할 수 있으며 정적 멤버는 가질 수 없음
- 가독성을 위해 짧게 작성 권장
지역 클래스 예시
정리
중첩 클래스에는 네 가지 종류가 있으며 각각의 용도가 다릅니다.
만약 중첩 클래스를 메서드 외부에서 사용해야 하거나 해당 메서드 내에 정의하기에는 길다면, 멤버 클래스로 생성하는 것이 좋습니다.
또한, 멤버 클래스의 각 인스턴스가 외부 인스턴스를 참조해야 한다면 비정적으로, 그렇지 않다면 정적으로 생성하는 것이 권장됩니다.
만약 중첩 클래스가 특정 메서드에서만 사용되며 해당 인스턴스를 생성하는 지점이 단 하나이고 이미 적합한 클래스나 인터페이스가 존재한다면 익명 클래스로 생성하는 것이 좋습니다.
그렇지 않은 경우, 지역 클래스로 생성하는 것을 권장합니다.
참고
이펙티브 자바
이펙티브 자바 완벽 공략 2부 - 백기선 강사님
https://tecoble.techcourse.co.kr/post/2020-11-05-nested-class/
반응형
'JAVA > Effective Java' 카테고리의 다른 글
[아이템 26] 로 타입은 사용하지 말라 (0) | 2024.02.16 |
---|---|
[아이템 25] 톱레벨 클래스는 한 파일에 하나만 담으라 (1) | 2024.02.09 |
[아이템 23] 태그 달린 클래스보다는 클래스 계층 구조를 활용하라 (0) | 2024.02.07 |
[아이템 22] 인터페이스는 타입을 정의하는 용도로만 사용하라 (0) | 2024.02.07 |
[아이템 21] 인터페이스는 구현하는 쪽을 생각해 설계하라 (1) | 2024.02.07 |