Inner & Nested Class

맥모닝·2023년 11월 21일
0

Kotlin

목록 보기
5/10

Inner Class

하나의 클래스 내부에 선언된 또 다른 클래스를 의미하며, 두 클래스가 서로 긴밀한 관계가 있거나 하나의 클래스 또는 메소드에서만 사용되는 클래스일 때 이용되는 기법이다.

Java Inner Class

Inner 클래스는 외부 참조를 한다.

  • 일반적으로 내부 인스턴스 클래스를 만들기 위해서는 먼저 외부 클래스를 초기화 후 내부 클래스를 초기화해야 한다.
    • 이러한 단계 과정 때문에 Inner 클래스는 자신을 만들어준 인스턴스에 대한 외부 참조를 갖게 된다.
  • 내부 클래스의 인스턴스 메소드에서 정규화된 this(클래스명.this)를 사용해 바깥 인스턴스의 메서드를 호출하거나 바깥 인스턴스의 참조를 가져올 수 있다.
class OuterClass {
   int field = 10;
   int getField() {
       return field;
   }

   class InnerClass {
       int getOuterfield() {
           return OuterClass.this.getField(); // 숨은 외부 참조가 있기 때문에 가능
       }
   }
}

public class Main {
    public static void main(String[] args) {
        // 1. 외부 클래스를 인스턴스화 해주기
        OuterClass outer = new OuterClass();
        // 2. 외부클래스.내부클래스 형식으로 내부 클래스를 초기화하여 사용할 수도 있다
        OuterClass.InnerClass inner = outer.new InnerClass();
        inner.getOuterfield();

        // 1 + 2
        OuterClass.InnerClass inner2 = new OuterClass().new InnerClass();
    }
}

Inner 클래스의 메모리 누수 현상

  • WHEN) Inner 클래스가 바깥 클래스를 외부 참조 함으로써, 만일 외부 클래스는 필요가 없어지고 내부 클래스만 남아있는 경우
  • WHY) 필요 없어진 외부 클래스를 GC(Garbage Collector) 대상으로 삼아 메모리에서 제거해야 하지만, 외부 참조로 내부 클래스와 연결되어 있기 때문에 메모리에서 제거가 안 되어 남아있게 된다. 이러한 경우 메모리 누수가 발생하여 프로그램의 성능이 저하될 수 있다.

HOW) 내부 클래스를 인스턴스가 아닌 static으로 설정하자!

  • 정적 멤버 클래스는 외부 인스턴스 없이 만들어질 수 있으므로 외부 참조가 존재하지 않는다.
    • 따라서 바깥 클래스 객체는 더이상 내부 클래스 객체와 아무런 관계가 아니게 되어 메모리 관리가 효율적으로 이루어진다.
class PocketBall {
    int size = 100;
    static int price = 5000;

    // static 내부 클래스
    static class PocketMonster {
        static String name = "이상해씨";
        int level = 10;

        // 외부 클래스 인스턴스 멤버 접근
        // 방법 : 내부 클래스 생성자에 외부 클래스 객체를 넘겨 접근
        public PocketMonster(PocketBall outer) {
            System.out.println(outer.size); // 100
        }

        public static void getPoketMember() {
            // 외부 클래스 인스턴스 멤버 접근 불가능
            // System.out.println(size);

            // 외부 클래스 정적 멤버 접근 가능
            System.out.println(price); // 5000

            // 내부 클래스 멤버도 정적 멤버만 접근 가능
            // System.out.println(level);

            System.out.println(name); // 이상해씨
        }
    }
}

public class Main {
    public static void main(String[] args) {
        // 정적 내부 클래스의 인스턴스는 외부 클래스를 먼저 생성하지 않아도 된다.
        PocketBall.PocketMonster poketmon = new PocketBall.PocketMonster(new PocketBall());
        System.out.println(poketmon.level); // 10
        System.out.println(PocketBall.PocketMonster.name); // 이상해씨

        // 클래스.정적내부클래스.정적메소드()
        PocketBall.PocketMonster.getPoketMember();
    }
}

Kotlin Inner Class

  • 코틀린은 자바와 정반대다. 자바는 기본적으로 중첩클래스를 생성하면 inner class로 정의되지만, 코틀린은 기본적으로 nested class로 정의된다.
    • 따라서 코틀린은 외부 클래스에 대한 참조를 저장하고 있지 않다.
class Outer {
    private val outerProperty = "Outer Property"

    class Inner(private val out: Outer) {
        fun accessOuterField(): String {
            return out.outerProperty
        }
    }
}

fun main() {
    val inner = Outer.Inner(Outer())
    print(inner.accessOuterField()) // Outer Property
}
  • inner 예약어를 붙여 외부 클래스에 대한 참조를 저장할 수 있다.
class Outer {
    val outerProperty = "Outer Property"

    inner class Inner {
        fun getOuterReference(): Outer = this@Outer
        
        // fun accessOuterField(): String = this@Outer.outerProperty
        fun accessOuterField(): String = outerProperty
    }
}

fun main() {
    val outer = Outer()
    val inner = outer.Inner()
    
    // Inner 클래스를 통해 외부 클래스의 인스턴스에 접근
    println(inner.accessOuterField()) // Outer Property

    // Inner 클래스를 통해 Outer 클래스의 참조에 접근
    val outerReference = inner.getOuterReference()
    println(outerReference.outerProperty) // Outer Property
}

참고 사이트

profile
필요한 내용을 공부하고 저장합니다.

0개의 댓글