effective java item 24. 멤버 클래스는 되도록 static 으로…
Nested class : static vs non-static
- 특정 클래스 내부에서만 사용할 목적으로 클래스를 정의할 때 nested class 를 정의하곤 한다. 그런데 지금까지 나는 무의식적으로 static nested class 로만 정의했는데, outer class 와 인스턴스 생성, 메모리 공간에 이유가 있었다.
- Oracle Java Tutorials 에 나온 설명
Terminology: Nested classes are divided into two categories: static and non-static. Nested classes that are declared static are called static nested classes. Non-static nested classes are called inner classes.
class OuterClass { static class StaticNestedClass {} class InnerClass {} }
- Nested class 는 크게 static 과 non-static 으로 구분할 수 있고, static 으로 선언된 Nested class 를 static nested class 라고 부른다. 그리고 non-static nested class 를 inner class 라고 부른다.
non-static member class, inner class
- non-static 멤버 클래스의 인스턴스는 outer class 의 인스턴스와 암묵적으로 연결된다. 그래서 non-static nested class(inner class) 에서 outer class 를 참조할 수 있다.
클래스명.this
형태로 바깥 클래스의 인스턴스 참조를 가져올 수 있다. - 바깥 인스턴스의 생성 없이 non-static member class 의 인스턴스도 생성할 수 없다. 즉, outer class 의 인스턴스 없이 생성할 수 없고, 바깥 클래스의 인스턴스 메소드에서 inner class 의 인스턴스를 생성하는 방법이 일반적이다. outer class 의 인스턴스를 이용하여
outerInstance.new XClass()
로 수동으로 생성할 수도 있지만, 외부에서 non-static nested class 를 사용한다면 이 경우는 차라리 새로운 Outer class 로 빼서 정의하는게 낫겠다. - inner class 에서 outer class 의 멤버에 접근
- 오라클 가이드를 보니 아래 예시를 Shadowing 이라는 단어로 설명하고 있고, static nested class 에서는 다음과 같이 접근할 수 없다. non-static nested class(inner class) 에서는
XXXClass.this.x
처럼 outer class 의 멤버 변수에 접근이 가능하다. Effective Java 에서는 아래 처럼 멤버 클래스에서 바깥 인스턴스에 접근할 일이 없다면 무조건 static 을 붙여서 static nested class 로 만들라고 한다.
public class ShadowTest { public int x = 0; class FirstLevel { public int x = 1; void methodInFirstLevel(int x) { System.out.println("x = " + x); System.out.println("this.x = " + this.x); System.out.println("ShadowTest.this.x = " + ShadowTest.this.x); } } public static void main(String... args) { ShadowTest st = new ShadowTest(); ShadowTest.FirstLevel fl = st.new FirstLevel(); fl.methodInFirstLevel(23); } }
inner class 단점 정리
- 오라클 가이드를 보니 아래 예시를 Shadowing 이라는 단어로 설명하고 있고, static nested class 에서는 다음과 같이 접근할 수 없다. non-static nested class(inner class) 에서는
- shadowing 예시를 보면 멤버 클래스가 outer class 에 대한 숨은 참조를 가지게 된다. 이 참조를 저장하려면 시간과 공간이 소비된다. 그리고
가비지 컬렉션이 outer class 의 인스턴스를 수거하지 못하는 메모리 누수가 생길 수 있다는 문제가 있다.
static nested class
- static nested class 는 outer class 인스턴스 생성 없이 독립적으로 존재할 수 있다.
- 대표적인 static nested class 의 예로 Map 과 Entry 의 관계가 있다. Map 인스턴스는 key-value 을 를 직접 관리하지 않고, 키-값 쌍을 표현하는 Entry 를 이용한다. Entry 가 매번 생성될때마다 Map(Outer class) 에 대한 참조를 각각 가지게 된다면 메모리 생성 및 관리에서 비효율적인 것이다. 그래서 Entry 는 static nested class 로 정의되어 있다.
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable { static class Node<K,V> implements Map.Entry<K,V> { } }