Idealim
article thumbnail

/* 본 게시물은 ' Do it 코틀린 프로그래밍 | with 황영덕 ' 의 내용을 토대로 작성되었습니다. */


#내부 클래스 기법

코틀린은 2가지의 내부 클래스 기법이 있다. 첫 번째는 중첩 클래스, 또 다른 하나는 이너 클래스이다. 중첩 클래스와 이너 클래스는 둘다 특정 클래스 내부에 있는 것이지만 사용 방법이 약간 다르다. 

그럼 왜 클래스 내부에 또 다른 클래스를 설계하여 둘까? 

그 이유는 독립적인 클래스로 정의하기 모호한 경우나 다른 클래스에서는 잘 사용하지 않는 내부에서만 사용하고 외부에서는 접근할 필요가 없기 때문이다. 

1. 중첩 클래스

코틀린에서 중첩 클래스는 기본적으로 정적(static) 클래스처럼 다뤄진다. 즉, 중첩 클래스는 객체 생성 없이 접근할 수 있다. 중첩 클래스를 사용해보자.

class Outer {
    val num1 = 5
    class Nested{
        val num2 = 10
        fun inside() : String {
            return "[Nested] Hello! $num2" // 외부 num1 에는 접근 불가
        }
    }
    fun outside() {
        val msg = Nested().inside() // 객체 생성 없이 중첩 클래스의 메서드 접근
        println("[Outer]: $msg, ${Nested().num2}") // 중첩 클래스의 프로퍼티의 접근
    }
}

fun main() {
    //static 처럼 객체 생성 없이 사용
    val output = Outer.Nested().inside()
    println(output)

    val outer = Outer() // 외부 클래스는 객체를 생성해야됨.
    outer.outside()
}

실행 결과

  • 중첩 클래스로 있는 경우 중첩 클래스(Nested)에서 외부 클래스(Outer)의 프로퍼티의 접근할 수 없다. 단, companion 객체는 접근 가능.
  • 외부 클래스에서 중첩 클래스의 프로퍼티에 접근할 수 있다.
  • 중첩 클래스는 외부에서 static 처럼 객체 생성 없이 사용할 수 있다.
왜 중첩 클래스에서 컴페니언 객체는 접근가능할까?
Nested 클래스에서 바로 바깥 클래스인 Outer 프로퍼티인 num3에 접근하고 있다. 이것이 가능한 이유는 컴페이니언 객체로 지정되어 객체 생성 없이 고정적인 메모리를 가지기 때문이다.
만약 Nested 클래스에서 Outer 클래스에 프로퍼티에 접근하고 싶으면 어떻게 해야할까?
class Nested 앞에 inner 키워드를 붙여주면 외부 클래스의 프로퍼티에 접근가능하다.

2. 이너 클래스

이너(내부) 클래스는 단순히 내부에 작성된 중첩 클래스와는 좀 다른 역할을 한다. 클래스 안에 이너 클래스를 정의할 수 있는데 이때 이너 클래스는 바깥 클래스의 멥버들에 접근할 수 있다. 심지어 private멤버도 접근이 가능하다. 이너 클래스를 사용해보자.

class Outer2(val outerStr: String){
    private val num1 = 1

    inner class Inner(){
        fun get(): String {
            return "입력한 생성자 값: $outerStr, 외부 클래스 프로퍼티 : $num1"
        }
    }
}

fun main() {
    val inner = Outer2("hello").Inner().get()
    println(inner)
}

실행 결과

3. 지역 클래스

지역 클래스는 특정 메서드의 블록이나 init 블록과 같이 블록 범위에서만 유효한 클래스이다. 블록 범위를 벗어나면 더 이상 사용되지 않는다. 지역 클래스를 사용해보자.

class Outer2(val outerStr: String){
    private val num1 = 1

    inner class Inner(){
        fun get(): String {

            class Local(){ // 지역 클래스 선언
                fun getNum1() = "[Local] num1 : $num1" // 외부 프로퍼티 접근가능
            }
            return "지역 클래스 프로퍼티 : ${Local().getNum1()}"
        }
    }
}

fun main() {
    val inner = Outer2("hello").Inner().get()
    println(inner)
}

실행 결과

여기서 사용된 Local 클래스는 get()에서만 유효한 클래스이다. 단 지역 클래스에서 외부의 멤버인 프로퍼티에 접근가능하다.

4. 익명 객체

자바에서는 익명 이너 클래스라는 것을 제공해 일회성으로 객체를 생성해 사용한다. 코틀린에서는 object 키워드를 사용하는 익명 객체로 이와 같은 기능을 수행한다. 자바와 다른 점은 익명 객체 기법으로 앞에서 살펴본 다중의 인터페이스를 구현할 수 있다는 것이다. 인터페이스를 만들고 이것을 구현하는 익명 객체를 만들어 보자.

interface Interface1 {
    fun on(): String
}


class Outer2(val outerStr: String){
    private val num1 = 1

    inner class Inner(){
        fun get(): String {

            class Local(){ // 지역 클래스 선언
                fun getNum1() = "[Local] num1 : $num1" // 외부 프로퍼티 접근가능
            }

            val obj = object: Interface1{
                override fun on(): String {
                    return "[object]: ${Local().getNum1()}"
                }
            }
            return obj.on() // 익명 객체 메서드 사용
//            return "입력한 생성자  값: $outerStr, 외부 클래스 프로퍼티 : $num1, 지역 클래스 프로퍼티 : ${Local().getNum1()}"
        }
    }
}

fun main() {
    val inner = Outer2("hello").Inner().get()
    println(inner)
}

실행 결과

on() 메서드가 호출될 때 마다 일회성 객체의 인스턴스가 만들어진다.

 

반응형
profile

Idealim

@Idealim

읽어주셔서 감사합니다. 잘못된 내용이 있으면 언제든 댓글로 피드백 부탁드립니다.