Notice
Recent Posts
Recent Comments
Link
«   2026/08   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31
Archives
Today
Total
관리 메뉴

undefined

[JAVA] Inner class사용 시 주의할 점 본문

JAVA & KOTLIN

[JAVA] Inner class사용 시 주의할 점

un-defined 2024. 1. 7. 03:47

내부 클래스(Inner class)를 사용할 때 주의해야하는 이유를 알아보자.

 

내부 클래스 종류

1. 정적 내부 클래스 (static)

외부 클래스 인스턴스가 아닌 클래스 자체에 속하는 클래스로 외부 클래스의 멤버 변수만 접근 가능하다.

public class Outer {
    static class StaticInner {
        // ...
    }
}

 

2. 멤버클래스 (non-static)

외부 클래스의 '인스턴스'에 속함. 외부 클래스의 모든 멤버에 접근 가능.

여기서 메모리 누수가 발생할 수 있다.

public class Outer {
    class Inner {
        // ...
    }
}

 

3. 지역클래스 

지역변수처럼 함수가 실행되는 동안에만 사용 가능하다.

public class Outer {
    void someMethod() {
        class LocalInner {
            // ...
        }
    }
}

 

4. 익명클래스

한번만 쓰고 버릴 클래스를 만들 때 사용. 주로 인터페이스를 구현하거나 추상클래스 사용받아서 사용

여기서도 외부 클래스의 변수에 접근할 수 있고, 역시 메모리 누수가 발생할 수 있다.

new Thread(new Runnable() {
    public void run() {
        // ...
    }
}).start();

 

 

그런데 메모리 누수가 어떻게 발생한다는건지 이해가 되지 않았다. 

 

그래서 내부클래스가 어떻게 메모리 누수를 발생시키는데?

비 정적 멤버 클래스를 생성하려면 반드시 외부 클래스의 인스턴스가 필요하다. 즉 내부 클래스 인스턴스는 외부 클래스 인스턴스에 대한 참조를 가지고 있어야 한다.

 

알겠는데 와닿지 않아.. 눈으로 확인해보자.

public class Outer {
    private int value = 10;

    public class Inner {
        public int getValue() {
            return value;
        }
    }
}

이 코드가 컴파일되면,

public class Outer {
    private int value = 10;

    public class Inner {
        final Outer this$0;  // 컴파일러가 추가한 참조

        public Inner() {
            this$0 = Outer.this;  // 생성자에서 외부 클래스에 대한 참조를 초기화
        }

        public int getValue() {
            return this$0.value;
        }
    }
}

위처럼 되는데, this$0이 outer클래스에 대한 참조이다. 그래서 inner에서 이 외부 클래스의 모든 변수에 접근할 수 있는 것!

 

익명클래스도 마찬가지.

public class OuterClass {
    private int value = 10;

    public void someMethod() {
        Thread thread = new Thread(new Runnable() {  // Runnable 인터페이스를 구현하는 익명 클래스
            @Override
            public void run() {
                System.out.println(value);  // 외부 클래스의 멤버 변수에 접근
            }
        });
        thread.start();
    }
}
public class OuterClass {
    private int value = 10;

    public void someMethod() {
        class AnonymousClass implements Runnable {
            final OuterClass this$0;  // 컴파일러가 추가한 참조

            AnonymousClass() {
                this$0 = OuterClass.this;  // 생성자에서 외부 클래스에 대한 참조를 초기화
            }

            @Override
            public void run() {
                System.out.println(this$0.value);
            }
        }

        Thread thread = new Thread(new AnonymousClass());
        thread.start();
    }
}

위 코드를 보면 AnonymousClass의 생성자에서 참조가 초기화되며, 이를 통해 AnonymousClass에서 OuterClass의 멤버 변수 value에 접근할 수 있게 된다.

 

코드를 디컴파일하면 컴파일러가 추가한 참조를 볼 수 있다고 한다

메모리 누수를 막으려면?

당연하지만 명시적으로 참조를 제거하면 된다.

public class OuterClass {
    public class InnerClass {
        // ...
    }

    public void someMethod() {
        InnerClass inner = new InnerClass();
        // ... 
        // inner 인스턴스가 더 이상 필요하지 않으므로 null로 설정
        inner = null;
    }
}

근데 이런 방법은 실수할 가능성이 높아서 피하라고 한다.

 

그 외에 정적 내부 클래스를 사용하거나,WeakReference를 사용하거나, Android에서 제공하는 Lifecycle-aware Component를 사용하는 방법도 있다. 는데 그건 다음에 알아보자

반응형
Comments