본문 바로가기

JAVA/Effective Java

item 24) 멤버 클래스는 되도록 static으로 만들라

728x90

1. 중첩 클래스(Nested Class)

 

- 다른 클래스 안에 정의된 클래스 (자신을 감싼 바깥 클래스 에서만 사용되어야 하며, 그 외의 쓰임새가 잇다면 톱레벨 클래스로 따로 클래스 파일에 정의해야함

 

종류 

  • 정적 멤버 클래스
  • (비정적) 멤버 클래스
  • 익명 클래스
  • 지역 클래스

 

2. 각각의 특징

 

  • 정적 멤버 클래스 vs 비정적 클래스 (클래스 앞에 static 키워드가 붙냐 안붙냐 차이)

 

- 비정적 클래스는 바깥 클래스의 인스턴스와 암묵적으로 참조함(바깥 인스턴스의 메소드 및 참조 접근 가능)

- 바깥 인스턴스와 독립적인 존재로 만들고 싶다면 반드시 정적 멤버 클래스로 선언해야함(비 정적 클래스는 참조를 지우더라도 바깥 클래스가 계속해서 참조하므로 가비지 컬렉션이 메모리를 수거하지 못함)

- 비정적 멤버 클래스는 어댑터 패턴에서 자주 사용됨(다른 클래스를 깜싸 다른 인스턴스 처럼 보이게 하는 뷰)

- 정적 멤버 클래스는 바깥 클래스가 표현하는 구성요소를 나타날떄 사용

- 접근범위가  public 이나 protected가 될 경우 정적이냐 아니냐가 더 중요해짐

/* Hash Map에 존재하는 비정적 , 정적 멤버 클래스 */

final class KeySet extends AbstractSet<K> {
        public final int size()                 { return size; }
        public final void clear()               { HashMap.this.clear(); }
        public final Iterator<K> iterator()     { return new KeyIterator(); }
        public final boolean contains(Object o) { return containsKey(o); }
        public final boolean remove(Object key) {
            return removeNode(hash(key), key, null, false, true) != null;
        }
        public final Spliterator<K> spliterator() {
            return new KeySpliterator<>(HashMap.this, 0, -1, 0, 0);
        }
        public final void forEach(Consumer<? super K> action) {
            Node<K,V>[] tab;
            if (action == null)
                throw new NullPointerException();
            if (size > 0 && (tab = table) != null) {
                int mc = modCount;
                for (Node<K,V> e : tab) {
                    for (; e != null; e = e.next)
                        action.accept(e.key);
                }
                if (modCount != mc)
                    throw new ConcurrentModificationException();
            }
        }
    }
    
    /*맵은 노드를 구성요소로 사용하지만, 노드는 맵을 직접 사용할 일이 절대 없음 => 정적 멤버 클래스로 선언*/
     static class Node<K,V> implements Map.Entry<K,V> {
        final int hash;
        final K key;
        V value;
        Node<K,V> next;

        Node(int hash, K key, V value, Node<K,V> next) {
            this.hash = hash;
            this.key = key;
            this.value = value;
            this.next = next;
        }

        public final K getKey()        { return key; }
        public final V getValue()      { return value; }
        public final String toString() { return key + "=" + value; }

        public final int hashCode() {
            return Objects.hashCode(key) ^ Objects.hashCode(value);
        }

        public final V setValue(V newValue) {
            V oldValue = value;
            value = newValue;
            return oldValue;
        }

        public final boolean equals(Object o) {
            if (o == this)
                return true;
            if (o instanceof Map.Entry) {
                Map.Entry<?,?> e = (Map.Entry<?,?>)o;
                if (Objects.equals(key, e.getKey()) &&
                    Objects.equals(value, e.getValue()))
                    return true;
            }
            return false;
        }
    }

 

  • 익명 클래스 : 이름 없는 클래스

- 바깥 클래스의 멤버가 아님(선언과 동시에 인스턴스화)

- 클래스 이름이 필요한 검사 제한적 (instance of, getclass 등)

- 자바 8버전 이후부터는 람다를 주로 사용함 !

PriorityQueue <Integer> pq = new PriorityQueue<Integer>(new Comparator<>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o1-o2;
            }
        });

 

  • 지역 클래스 

-지역변수를 선언할수 있는 곳에서 사용 가능 (유효범위도 같음)

- 이름이 있고 반복적으로 사용가능하나 유효범위내에서만 사용가능 정적멤버 X (넷중에서 가장 드물게 사용됨)