본문 바로가기

이펙티브 자바

[이펙티브 자바] 아이템 24. 멤버 클래스는 되도록 static으로 만들라

중첩 클래스(nested class) : 다른 클래스 내부에 선언된 클래스.

 

중첩 클래스의 종류 4가지 : 

정적 멤버 클래스

정적 멤버 클래스는 바깥 클래스의 인스턴스와 독립적으로 존재할 수 있는 클래스다. 즉, 바깥 클래스의 인스턴스 없이도 생성하고 사용할 수 있다. 정적 멤버 클래스는 바깥 클래스의 정적 멤버에만 접근할 수 있으며, 바깥 클래스의 인스턴스 멤버에는 접근할 수 없다.

 

비정적 멤버 클래스

비정적 멤버 클래스는 바깥 클래스의 인스턴스와 연관되어 있는 클래스다. 이러한 클래스의 객체는 바깥 클래스의 객체와 연결되어 있으며, 바깥 클래스의 인스턴스 멤버와 메서드에 접근할 수 있다. 비정적 내부 클래스는 바깥 클래스의 인스턴스를 통해서만 생성할 수 있다. 정적 클래스와 의 구문적 차이는 단지 static이 붙어 있고 없고의 차이뿐이지만, 의미상 차이는 꽤 크다.

 

지역 클래스 (Local Class)

메서드 내부에 선언된 클래스로, 선언된 메서드 내에서만 사용할 수 있다.

 

익명 클래스 (Anonymous Class)

이름이 없는 클래스로, 주로 단일 인스턴스 생성에 사용된다.

 

public class OuterClass {
    private static int staticVar = 100;
    private int instanceVar = 200;

    // 정적 멤버 클래스
    static class StaticMemberClass {
        void display() {
            System.out.println(staticVar); // 정적 변수에 접근 가능
            // System.out.println(instanceVar); // 컴파일 에러: 인스턴스 변수에 접근 불가
        }
    }

    // 비정적 멤버 클래스
    class NonStaticMemberClass {
        void display() {
            System.out.println(staticVar); // 정적 변수에 접근 가능
            System.out.println(instanceVar); // 인스턴스 변수에 접근 가능
        }
    }

    // 메서드 내에서 지역 클래스 사용
    void methodWithLocalClass() {
        // 지역 클래스 정의
        class LocalClass {
            void display() {
                System.out.println("Inside Local Class. staticVar: " + staticVar + ", instanceVar: " + instanceVar);
            }
        }

        // 지역 클래스 인스턴스 생성 및 사용
        LocalClass localInstance = new LocalClass();
        localInstance.display();
    }

    // 메서드 내에서 익명 클래스 사용
    void methodWithAnonymousClass() {
        // 익명 클래스를 통한 Runnable 인터페이스 구현
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("Inside Anonymous Class. staticVar: " + staticVar);
                // instanceVar 접근 가능 (익명 클래스는 비정적 컨텍스트 내에 정의되었기 때문)
                System.out.println("instanceVar: " + instanceVar);
            }
        };
        
        // 익명 클래스 인스턴스 사용
        new Thread(runnable).start();
    }

    public static void main(String[] args) {
        OuterClass outerInstance = new OuterClass();

        // 정적 멤버 클래스 인스턴스 생성 및 사용
        OuterClass.StaticMemberClass staticInstance = new OuterClass.StaticMemberClass();
        staticInstance.display();

        // 비정적 멤버 클래스 인스턴스 생성 및 사용
        OuterClass.NonStaticMemberClass nonStaticInstance = outerInstance.new NonStaticMemberClass();
        nonStaticInstance.display();

        // 지역 클래스 사용
        outerInstance.methodWithLocalClass();

        // 익명 클래스 사용
        outerInstance.methodWithAnonymousClass();
    }
}

 

결론 : 중첩 클래스에는 네 가지가 있으며, 각각의 쓰임이 다르다.

메서드 밖에서도 사용해야하거나 메서드 안에 정의하기엔 너무 길다면 멤버 클래스로 만든다.

멤버 클래스의 인스턴스 각각이 바깥 인스턴스를 참조한다면 비정적으로, 그렇지 않으면 정적으로 만들자.

중첩 클래스가 한 메서드 안에서만 쓰이면서 그 지점이 단 한곳이고 해당 타입으로 쓰기에 적합한 클래스나 인터페이스가 이미 있다면 익명 클래스로 만들고, 그렇지 않으면 지역 클래스로 만들자.