반응형
이 포스팅에서 작성하는 내용은 EFFECTIVE JAVA(이펙티브자바) 에서 발췌하였습니다.
아이템 24. 멤버 클래스는 되도록 static으로 만들라
중첩 클래스(nested class)
- 중첩 클래스(nested class)란 다른 클래스 안에 정의된 클래스를 말한다.
- 중첩 클래스는 자신을 감싼 바깥 클래스에서만 쓰여야 하며, 그 외의 쓰임새가 있다면 톱레벨 클래스로 만들어야 한다.
- 종류로는 정적 멤버 클래스, (비정적) 멤버 클래스, 익명 클래스, 지역 클래스
- 위 중 첫 번째를 제외한 나머지는 내부 클래스(inner class)에 해당한다.
정적 멤버 클래스
- 다른 클래스 안에 선언되고, 바깥 클래스의 private 멤버에도 접근할 수 있다는 점만 제외하고는 일반 클래스와 똑같다.
(다른 정적 멤버와 똑같은 접근 규칙을 적용받는다.) - 흔히 바깥 클래스와 함께 쓰일 때만 유용한 public 도우미 클래스로 쓰임
- 계산기가 지원하는 연산 종류를 정의하는 열거 타입을 예로 들면,
- Operation 열거 타입은 Calculator 클래스의 public 정적 멤버 클래스가 되어야 한다.
- 그러면 Calculator의 클라이언트에서 Calculator.Operation.PLUS or Calculator.Operation.MINUS처럼 사용 가능
비정적 멤버 클래스
- 정적 멤버 클래스와 static이 붙어있고 없고의 차이.
- 비정적 멤버 클래스의 인스턴스는 바깥 클래스의 인스턴스와 암묵적으로 연결됨
- 비정적 멤버 클래스의 인스턴스 메서드에서 정규화된 this를 사용해 바깥 인스턴스의 메서드를 호출하거나 바깥 인스턴스의 참조를 가져올 수 있음
- 정규화된 this? 클래스명.this 형태로 바깥 클래스의 이름을 명시하는 용법
- 개념상 중첩 클래스의 인스턴스가 바깥 인스턴스와 독립적으로 존재할 수 있다면 정적 멤버 클래스로 만들어야 함.
비정적 멤버 클래스는 바깥 인스턴스 없이는 생성할 수 없기 때문 - 비정적 멤버 클래스의 인스턴스와 바깥 인스턴스 사이의 관계는 멤버 클래스가 인스턴스화 될 때 확립되며, 더 이상 변경할 수 없음
- 이 관계 정보는 비정적 멤버 클래스의 인스턴스 안에 만들어져 메모리 공간을 차지하며, 생성 시간도 더 걸린다.
- 비정적 멤버 클래스는 어댑터를 정의할 때 자주 쓰임
즉, 어떤 클래스의 인스턴스를 감싸 다른 클래스의 인스턴스처럼 보이게 하는 뷰로 사용 - Map 인터페이스의 구현체들은 보통 자신의 컬렉션 뷰를 구현할 때 비정적 멤버 클래스를 사용
- Set과 List 같은 다른 컬렉션 인터페이스 구현들도 자신의 반복자를 구현할 때 비정적 멤버 클래스를 주로 사용
ex 1) 비정적 멤버 클래스의 흔한 쓰임 - 자신의 반복자 구현
public class MySet<E> extends AbstractSet<E> {
... // 생략
@Override
public Iterator<E> iterator() {
return new MyIterator();
}
private class MyIterator implements Iterator<E> {
...
}
}
- 멤버 클래스에서 바깥 인스턴스에 접근할 일이 없다면 무조건 static을 붙여서 정적 멤버 클래스로 만들자.
- static을 생략하면 바깥 인스턴스로의 숨은 외부 참조를 갖게 됨. (메모리 누수 발생 가능)
private 정적 멤버 클래스
- 흔히 바깥 클래스가 표현하는 객체의 한 부분을 나타낼 때 사용
- 키와 값을 매핑시키는 Map 인스턴스를 생각해보자.
- 많은 Map 구현체는 각각의 키-값 쌍을 표현하는 엔트리 객체들을 가지고 있다.
- 모든 엔트리가 Map과 연관되어 있지만, 엔트리 메서드들은 Map을 직접 사용하지 않아 엔트리를 비정적 멤버 클래스로 표현하는 것은 낭비이며 private 정적 멤버 클래스가 적절해보인다.
익명 클래스
- 익명 클래스는 바깥 클래스의 멤버가 아니다.
멤버와 달리, 쓰이는 시점에 선언과 동시에 인스턴스가 만들어진다. (코드 어디에서든 만들 수 있으며, 비정적인 문맥에서 사용될 때만 바깥 클래스의 인스턴스를 참조할 수 있다.) - 정적 문맥에서라도 상수 변수 이외의 정적 멤버는 가질 수 없다.
(상수 표현을 위해 초기화된 final 기본 타입과 문자열 필드만 가질 수 있음) - 응용하는데 제약이 많다.
- 선언한 지점에서만 인스턴스를 만들 수 있고, instanceof 검사나 클래스의 이름이 필요한 작업은 수행 X
- 여러 인터페이스를 구현할 수 없고, 인터페이스를 구현하는 동시에 다른 클래스 상속 불가
- 익명 클래스를 사용하는 클라이언트는 그 익명 클래스가 상위 타입에서 상속한 멤버 외에는 호출 X
- 자바가 람다를 지원하기 이전에는 즉석에서 작은 함수 객체나 처리 객체를 만드는 데 익명 클래스를 주로 사용함
- 익명 클래스의 또 다른 주 쓰임은 정적 팩터리 메서드를 구현하는 경우이다.
지역 클래스
- 네 가지 중첩 클래스 중 가장 드물게 사용
- 지역 변수를 선언할 수 있는 곳이면 선언 가능(유효 범위도 지역 변수와 같음)
- 멤버 클래스처럼 이름이 있고, 반복해서 사용 가능
- 비정적 문맥에서 사용될 때만 바깥 인스턴스를 참조할 수 있으며, 정적멤버는 가질 수 없고, 가독성을 위해 짧게 작성해야함.
정리
- 중첩 클래스에는 네 가지가 있으며, 각각 쓰임이 다르다.
- 메서드 밖에서도 사용해야 하거나 메서드 안에 정의하기엔 너무 길다면 멤버클래스로 만듬
- 멤버 클래스와 인스턴스 각각이 바깥 인스턴스를 참조한다면 비정적으로, 그렇지 않으면 정적으로 만듬
- 중첩 클래스가 한 메서드 안에서만 쓰이면서 그 인스턴스를 생성하는 지점이 단 한곳이고 해당 타입으로 쓰기에 적합한 클래스나 인터페이스가 이미 있다면 익명 클래스로 만들고, 그렇지 않으면 지역 클래스로 만들자.
반응형
'BE > Java' 카테고리의 다른 글
[Effective Java] 아이템 26. 로 타입은 사용하지 말라 (0) | 2022.10.06 |
---|---|
[Effective Java] 아이템 25. 톱레벨 클래스는 한 파일에 하나만 담으라 (0) | 2022.10.05 |
[Effective Java] 아이템 23. 태그 달린 클래스보다는 클래스 계층구조를 활용하라 (1) | 2022.09.30 |
[Effective Java] 아이템 22. 인터페이스는 타입을 정의하는 용도로만 사용하라 (0) | 2022.09.29 |
[Effective Java] 아이템 21. 인터페이스는 구현하는 쪽을 생각해 설계하라 (0) | 2022.09.28 |