-
Item 24. 멤버 클래스는 되도록 static으로 만들라Book/Effective Java 3E 2022. 11. 6. 09:25반응형
중첩 클래스(nested class)란 다른 클래스 안에 정의된 클래스를 말합니다. 중첩 클래스는 자신을 감싼 바깥 클래스에서만 쓰여야 하며, 그 외의 쓰임새가 있다면 톱레벨 클래스로 만들어야 합니다.
중첩 클래스의 종류
- 정적 멤버 클래스
- (비정적) 멤버 클래스
- 익명 클래스
- 지역 클래스
이 중 첫번째를 제외한 나머지는 내부 클래스(inner class)에 해당합니다.
정적 멤버 클래스
정적 멤버 클래스는 다른 클래스 안에 선언되고, 바깥 클래스의 private 멤버에도 접근할 수 있다는 점만 제외하고는 일반 클래스와 똑같습니다. 정적 멤버 클래스는 흔히 바깥 클래스와 함께 쓰일 때만 유용한 public 도우미 클래스로 쓰입니다.
public class OuterClass { private int x = 10; // 정적 멤버 클래스 public static class StaticMemberClass { void effectiveJava() { OuterClass outerClass = new OuterClass(); // 바깥 클래스 private 필드 접근 outerClass.x = 20; } } }
비정적 멤버 클래스
정적 멤버 클래스와 비정적 멤버 클래스의 구문상 차이는 단지 static이 붙어 있고 없고 뿐이지만, 의미상 차이는 의외로 꽤 큽니다. 비정적 멤버 클래스의 인스턴스는 바깥 클래스의 인스턴스와 암묵적으로 연결됩니다. 그래서 비정적 멤버 클래스의 인스턴스 메서드에서 정규화된 this를 사용해 바깥 인스턴스의 메서드를 호출하거나 바깥 인스턴스의 참조를 가져올 수 있습니다. 따라서 개념상 중첩 클래스의 인스턴스가 바깥 인스턴스와 독립적으로 존재할 수 있다면 정적 멤버 클래스로 만들어야 합니다.
public class OuterClass { void outer() { ... } // 비정적 멤버 클래스 class NonStaticMemberClass { void effectiveJava() { OuterClass.this.outer(); } } }
비정적 멤버 클래스는 어댑터[Gamma95]를 정의할 때 자주 쓰입니다. 즉, 어떤 클래스의 인스턴스를 감싸 마치 다른 클래스의 인스턴스처럼 보이게 하는 뷰로 사용하는 것입니다. 예컨대 Map 인터페이스의 구현체들은 보통 (keySet, entrySet, values 메서드가 반환하는) 자신의 컬렉션 뷰를 구현할 때 비정적 멤버 클래스를 사용합니다. 비슷하게, Set과 List 같은 다른 컬렉션 인터페이스 구현들도 자신의 반복자를 구현할 때 비정적 멤버 클래스를 주로 사용합니다.
// 코드 24-1 비정적 멤버 클래스의 흔한 쓰임 - 자신의 반복자 구현 public class MySet<E> extends AbstractSet<E> { ... // 생략 @Override public Iterator<E> iterator() { return new MyIterator(); } private class MyIterator implements Iterator<E> { ... } }
멤버 클래스에서 바깥 인스턴스에 접근할 일이 없다면 무조건 static을 붙여서 정적 멤버 클래스로 만드는 게 좋습니다. static을 생략하면 바깥 인스턴스로의 숨은 외부 참조를 갖게 되고, 이 참조를 저장하려면 시간과 공간이 소비됩니다. 더 심각한 문제는 가비지 컬렉션이 바깥 클래스의 인스턴스를 수거하지 못하는 메모리 누수가 생길 수 있다는 점입니다(Item 7).
익명 클래스
익명 클래스는 이름이 없는 클래스로 쓰이는 시점에 선언과 동시에 인스턴스가 만들어집니다. 또한 익명 클래스는 바깥 클래스의 멤버도 아니며 코드의 어디서든 만들 수 있습니다. 익명 클래스는 표현식 중간에 등장하므로 (10줄 이하로) 짧지 않으면 가독성이 떨어집니다.
자바가 람다를 지원하기 전에는 즉석에서 작은 함수 객체나 처리 객체(process object)를 만드는 데 익명 클래스를 주로 사용했으나, 이제는 람다에게 그 자리를 물려줬습니다(Item 42). 익명 클래스의 또 다른 주 쓰임은 정적 팩터리 메서드를 구현할 때입니다(코드 20-1의 intArrayAsList 참조)
지역 클래스
지역 클래스는 지역변수를 선언할 수 있는 곳이면 실질적으로 어디서든 선언할 수 있고, 유효범위도 지역변수와 같습니다.
public class OuterClass { void x() { // 지역 클래스 class LocalClass { void effectiveJava() { System.out.println("LocalClass"); } } } }
중첩 클래스에는 네 가지가 있으며, 각각의 쓰임새가 다르다.
메서드 밖에서도 사용해야 하거나 메서드 안에 정의하기엔 너무 길다면 멤버 클래스로 만든다.
멤버 클래스의 인스턴스 각각이 바깥 인스턴스를 참조한다면 비정적으로, 그렇지 않으면 정적으로 만들자.
중첩 클래스가 한 메서드 안에서만 쓰이면서 그 인스턴스를 생성하는 지점이 단 한곳이고 해당 타입으로 쓰기에 적합한 클래스나 인터페이스가 이미 있다면 익명 클래스로 만들고, 그렇지 않으면 지역 클래스로 만들자.
[참고 정보]
이펙티브 자바 Effective Java 3/E 도서 [조슈아 블로크 저]
반응형'Book > Effective Java 3E' 카테고리의 다른 글
Item 26. 로 타입은 사용하지 말라 (0) 2022.11.08 Item 25. 톱레벨 클래스는 한 파일에 하나만 담으라 (0) 2022.11.06 Item 23. 태그 달린 클래스보다는 클래스 계층구조를 활용하라 (0) 2022.11.02 Item 22. 인터페이스는 타입을 정의하는 용도로만 사용하라 (0) 2022.11.02 Item 21. 인터페이스는 구현하는 쪽을 생각해 설계하라 (0) 2022.10.29