Kotlin - 중첩 클래스(Nested classes)에 대해서 알아보기

중첩(nested) 클래스는 클래스 안에 있는 클래스를 말합니다. 아래 코드에서 OuterClass 내부에는 NestedClass가 존재하는 형태인데요. NestedClass를 중첩클래스라고 합니다.

class OuterClass {
    ...
    class NestedClass {
        ...
    }
}

중첩(Nested) 클래스는 크게 두종류로 나눌 수 있습니다.

  • Static nested class(정적 중첩 클래스)
  • Non-static nested class(inner class라고 합니다, 비정적 중첩 클래스)

자바에 익숙하신 분들을 위해, 자바와 코틀린의 코드를 비교하면서 위의 중첩 클래스들에 대해서 설명하겠습니다.

Static Nested Class (정적 중첩 클래스)

정적 중첩 클래스의 특징은 다음과 같습니다.

  • OuterClass의 지역변수의 접근이 불가능
  • 외부에서 OuterClass.NestedClass 로 객체 생성 가능

먼저 자바는 아래처럼 사용할 수 있습니다. 중첩 클래스에 static 키워드를 붙여 구현해야 합니다.

class OuterClass {
    ...
    static class StaticNestedClass {
        ...
    }
}

코틀린은 아래처럼 사용할 수 있습니다. 자바와는 다르게 키워드를 아무것도 붙이지 않아야 합니다.

class OuterClass {
    ...
    class StaticNestedClass {
        ...
    }
}

아래 코드는 위의 스켈레톤 코드에 조금 더 구현 내용을 추가하였습니다. 위에서 설명한 것처럼, 정적 중첩 클래스는 외부에서 생성이 가능합니다. 예제에서는 OuterClass.StaicNestedClass로 접근하여 객체를 생성할 수 있습니다. 하지만 정적이기 때문에 OuterClass의 내부 변수에 접근이 불가능합니다. printItems()에서 OuterClass.outerValue를 접근하려고 하면 컴파일 에러가 발생합니다.

class OuterClass {
    val outerValue = 10
    class StaicNestedClass {
        private val innerValue = 20
        fun printItems() {
            println("value: $innerValue")
        }
    }
}

fun main(args: Array<String>) {
    val nested = OuterClass.StaicNestedClass()
    nested.printItems()
}
# 결과
value: 20

Non-static Nested Class, Inner Class(비정적 중첩 클래스)

비정적 중첩 클래스(Non-static nested class)는 Inner class라고 합니다.

특징은 다음과 같습니다.

  • OuterClass의 지역변수 접근이 가능
  • 외부에서 OuterClass.NestedClass 로 객체 생성 불가능

먼저 자바에서는 아래와 같이 사용할 수 있습니다. 특별한 키워드를 붙이지 않았습니다.

class OuterClass {
    ...
    class InnerClass {
        ...
    }
}

코틀린에서는 아래와 같이 사용할 수 있습니다. inner class로 사용하려면 꼭 inner 키워드를 붙여야 합니다.

class OuterClass {
    ...
    inner class InnerClass {
        ...
    }
}

아래 코드는 위의 스켈레톤 코드에 구현을 조금 추가하였습니다. 우선 정적 중첩 클래스와 차이는 OuterClass 밖에서 객체를 중첩클래스 객체를 생성할 수 없다는 것입니다. InnerClass는 OuterClass의 내부에서 객체를 생성하였습니다. 다른 차이점은 InnerClass 내부에서 OuterClass의 내부변수에 접근이 가능합니다.

class OuterClass {
    val outerValue = 10
    inner class InnerClass {
        private val innerValue = 20
        fun printItems() {
            println("inner: $innerValue, outer: $outerValue")
        }
    }

    fun printItems() {
        val inner = InnerClass()
        inner.printItems()
    }
}

fun main(args: Array<String>) {
    val outer = OuterClass()
    outer.printItems()
}

InnerClass.printItems을 보시면 inner와 outer의 변수에 모두 접근하고 있습니다.

우연히 inner와 outer의 변수 이름이 동일한 경우가 있습니다. 아래 코드가 그런 경우인데요. Outer와 Inner는 모두 value라는 이름의 변수를 갖고 있습니다.

class OuterClass {
    val value = 10
    inner class InnerClass {
        private val value = 20
        fun printItems() {
            println("inner: $value or ${this.value} or ${this@InnerClass.value}")
            println("outer: ${this@OuterClass.value}")
        }
    }

    fun printItems() {
        val inner = InnerClass()
        inner.printItems()
    }
}
# 결과
inner: 20 or 20 or 20
outer: 10

InnerClass에서 value를 접근하면 OuterClass가 아닌 InnerClass의 value를 접근합니다. 마치 메소드를 Override하는 것처럼 현재 스코프가 InnerClass이고 동일한 이름의 변수가 Inner에도 있기 때문에 Inner를 우선적으로 사용합니다. OuterClass의 value를 접근하려면 변수명 앞에 this@클래스이름으로 OuterClass의 스코프에 있는 변수를 명시적으로 지정할 수 있습니다.

정리

코틀린의 중첩 클래스에 대해서 간략히 알아보았습니다. 크게 정적 중첩클래스와 비정적 중첩클래스로 나눌 수 있습니다. 비정적 중첩클래스에는 위에서 설명한 Inner class가 있습니다.

Inner class에는 지역(Local) 클래스, 익명(Anonymous) 클래스라고 불리는 것들도 있습니다. 익명 클래스는 자주 사용하던 것이고, 지역 클래스는 메소드 내부에 정의한 클래스를 말합니다. 이 부분은 따로 구글링해보시면 자료를 찾으실 수 있습니다.

참고

Loading script...
codechachaCopyright ©2019 codechacha