Java에서 static inner classs는 언제 사용해야 하나요?

Hyunta·2022년 11월 24일
0

학습동기

정적 클래스 관련해서 질문을 받았다. 내부 클래스와 정적 내부 클래스의 차이는 무엇이고 언제 사용하면 좋을지? 당시에는 잘 떠오르지 않아서 언제 정적 클래스를 활용하면 좋을지 모르겠어서 학습했다.

inner class vs static inner class

언제 활용할지를 논하기 전에 내부 클래스와 정적 내부 클래스의 차이를 알아보자. 공식문서 에 따르면 정확한 용어는 각각 Inner Class와 Nested Static Class 다.

public class OuterClass {

    String outerField = "Outer field";
    static String staticOuterField = "Static outer field";

    class InnerClass {
        void accessMembers() {
            System.out.println(outerField);
            System.out.println(staticOuterField);
        }
    }

    static class StaticNestedClass {
        void accessMembers(OuterClass outer) {
            // Compiler error: Cannot make a static reference to the non-static
            //     field outerField
            // System.out.println(outerField);
            System.out.println(outer.outerField);
            System.out.println(staticOuterField);
        }
    }
}

가장 큰 차이는 내부 클래스는 외부 클래스의 필드값을 사용할 수 없다는 것이다. 정적 내부 클래스에서 외부 클래스의 필드값을 사용하려면 외부 클래스 값을 주입 받아서 사용해야한다. 내부 클래스에서는 외부를 참조하고 있기 때문에 바로 사용할 수 있다.

클래스 로딩 시점의 차이

외부 참조가 언제 일어나는지 알아보기 위해서 클래스 로딩 시점을 찾아봤다. JVM은 ClassLoader를 통해서 class를 로딩하고 초기화한다. 클래스가 사용될 때 값이 초기화되어 Runtime Data Area에 올라간다.

class Outer {
    // static 변수
    static String value = "> Outer 클래스의 static 필드 입니다.";

    // static final 상수
    static final String VALUE = "> Outer 클래스의 static final 필드 입니다.";

    Outer() {
        System.out.println("> Outer 생성자 초기화");
    }

    // static 메서드
    static void getInstance() {
        System.out.println("> Outer 클래스의 static 메서드 호출");
    }

    // inner 클래스
    class Inner {
        Inner() {
            System.out.println("> Inner 생성자 초기화");
        }
    }

    // static inner 클래스
    static class Holder {
        static String value = "> Holder 클래스의 static 필드 입니다.";
        static final String VALUE = "> Holder 클래스의 static final 필드 입니다.";

        Holder() {
            System.out.println("> Holder 생성자 초기화");
        }
    }
}

해당 클래스를 사용해서 정적 내부 클래스와 내부 클래스의 로딩시점을 보자.

내부 클래스

public class Main {

    public static void main(String[] args) {
				new Outer().new Inner();
    }
}

외부 클래스를 생성하고, 내부 클래스를 생성하는 인스턴스화 과정이 2번 필요하다. 그렇기 때문에 Outer, Inner 모두 로드된다.

외부 클래스를 초기화한 뒤 내부 클래스를 초기화하기 때문에 외부 참조를 갖게 된다. 암묵적으로 두 클래스는 연결된다.

외부 클래스는 호출 용도로만 사용되지만 내부 클래스의 참조로 인해 GC가 정상적으로 접근할 수 없기 때문에 데이터를 수거해가지 못한다. 메모리 누수가 발생한다.

내부 정적 클래스

public class Main {

    public static void main(String[] args) {
				new Outer.Holder();
    }
}

내부 정적 클래스는 외부 클래스를 생성하지 않고 직접 인스턴스화 가능하다. 외부 클래스는 로드되지도 않고 따라서 외부 참조가 발생하지 않는다.

활용 방안

항상 내부 정적 클래스를 만드는 것이 메모리 누수를 방지할 수 있기 때문에 특별한 이유가 없다면 유리하다고 생각한다. 하지만 외부 클래스의 필드값을 내부 클래스에서 사용해야 한다면 내부 클래스를 사용하는 것이 상황에 따라서 적절할 수도 있을거라고 생각한다.

Reference

https://inpa.tistory.com/entry/JAVA-%E2%98%95-%ED%81%B4%EB%9E%98%EC%8A%A4%EB%8A%94-%EC%96%B8%EC%A0%9C-%EB%A9%94%EB%AA%A8%EB%A6%AC%EC%97%90-%EB%A1%9C%EB%94%A9-%EC%B4%88%EA%B8%B0%ED%99%94-%EB%90%98%EB%8A%94%EA%B0%80-%E2%9D%93?category=976278

https://tecoble.techcourse.co.kr/post/2020-11-05-nested-class/

https://johngrib.github.io/wiki/java-inner-class-may-be-static/

profile
세상을 아름답게!

0개의 댓글