중첩 클래스는 자신을 감싼 바깥 클래스에서만 써야한다.

중첩 클래스의 종류는 정적 멤버 클래스, 멤버 클래스, 익명 클래스, 지역 클래스 총 4가지다.

 

정적 멤버 클래스를 제외한 나머지 3가지는 내부 클래스(inner class)에 해당한다.

 

각각의 사용 용도에 대해 알아보자

정적 멤버 클래스, 비정적 멤버 클래스

흔히 바깥 클래스와 함께 쓰일 때만 유용한 public 도우미 클래스로 쓰인다.

예를 들어서

public class Calculator {

    public static enum Operator{
        PLUS,MINUS,MULTIPLY,DIVIDE
    }

}

위의 코드처럼 Calculator 안에 Operator라는 정적 멤버 클래스를 만들면

외부에서 Calculator.Operator.PLUS 같은 형태로 사용 할 수 있게 된다.

 

정적 멤버 클래스와 비정적 멤버 클래스의 차이는 static을 쓰냐 안쓰냐의 차이지만

의미상으로 큰 차이를 가지고 있다.

public class Test{   
    public int num;
    public static final number = 10;
}

이와 같이 멤버 변수가 선언되어 있을때 비정적 멤버 클래스는 Test.this의 형태로

바깥 인스턴스의 메소드를 호출하거나 바깥 인스턴스의 참조를 가져올 수 있다.

 

만약 비정적 멤버 클래스가 독립적으로 움직인다면 (바깥 인스터에 접근할 일이 없다면) static으로 만드는 것이 낫다.

static을 사용하지 않으면 바깥 인스턴스와 숨은 외부 참조를 갖게 되어 메모리상 누수가 발생할 수 있다.

 

private 정적 멤버 클래스는 흔히 바깥 클래스가 표현하는 객체의 한 부분(구성요소)을 나타낼 때 쓴다.

예를 들어 Map 구현체는 각각의 키-값 쌍을 표현하는 엔트리 객체들을 가지고 있다.

    /**
     * A map entry (key-value pair).  The {@code Map.entrySet} method returns
     * a collection-view of the map, whose elements are of this class.  The
     * <i>only</i> way to obtain a reference to a map entry is from the
     * iterator of this collection-view.  These {@code Map.Entry} objects are
     * valid <i>only</i> for the duration of the iteration; more formally,
     * the behavior of a map entry is undefined if the backing map has been
     * modified after the entry was returned by the iterator, except through
     * the {@code setValue} operation on the map entry.
     *
     * @see Map#entrySet()
     * @since 1.2
     */

    interface Entry<K, V> 

모든 엔트리가 맵과 연관되어 있지만 엔트리의 메소드들은 맵을 직접 사용하지 않기 때문에

비정적 멤버 클래스는 낭비고 private 정적클래스가 가장 알맞는다고 나와있지만

막상 찾아보니 public 정적 멤버 메소드로 되어있었다.

 

public냐 private이냐의 중요성에 대해선 잘모르겠지만 일단 정적 멤버 클래스로 해야한다는 것만 체크해두고 넘어가자!

public static <K extends Comparable<? super K>, V> Comparator<Map.Entry<K, V>> comparingByKey() {
            return (Comparator<Map.Entry<K, V>> & Serializable)
                (c1, c2) -> c1.getKey().compareTo(c2.getKey());
        }

익명클래스

익명클래스는 이름 그대로 이름이 없다.

또한 익명 클래스는 바깥 클래스의 멤버도 아니다.

멤버와 달리, 쓰이는 시점에 선언과 동시에 인스턴스가 만들어진다.

코드의 어디서든 만들 수 있다.

그리고 오직 비정적인 문맥에서 사용될 때만 바깥 클래스의 인스턴스를 참조할 수 있다.

정적 문맥에서라도 상수 변수 이외의 정적 멤버는 가질 수 없다.

즉, 상수 표현을 위해 초기화된 final기본 타입과 문자열 필드만 가질 수 있다.

라고 책에서 말하는데 사실 이해가 잘 가지 않는다.

 

자바 7이전에는 즉석에서 작은 함수 객체나 처리 객체를 위해 익명 클래스를 사용했지만

자바가 람다를 지원한 이후로 부터는 잘 사용하지 않는다고 한다.

 

익명클래스는 자바스크립트의 익명함수와 형태가 비슷하다.

Test test = new Test(){
        public int num = 1;
        @Override
        public int getNum(num){
            this.num = num;
        }
}

이런식으로 객체 생성시점에 바로 함수를 사용할 수 있는 것이다.

요새는 이렇게 만들면 인텔리J에서 알아서 람다형태로 바꿔주더라!

지역 클래스

네 가지 중첩 클래스 중 가장 드물게 사용된다.

지역클래슨는 지역변수를 선언할 수 있는 곳이면 실질적으로 어디서든 선언할 수 있고 유효범위도 지역변수와 같다.

예시를 가볍게 보자.

class Outer{

    public void sampleMethod(){
        class innerClass{...} //지역메소드
    }
}

+ Recent posts