코틀린 언어 정리 2-16

class 확장(Extensions)

특징(제한) 및 사례 분석

(계속)...

확장을 클래스의 멤버로 선언 ( 다른 클래스의 내부에 멤버처럼 선언하는 확장 함수 또는 프로퍼티 )

Declaring Extensions as Members ( Dispatch Receiver and Extension Receiver )

확장 함수나 프로퍼티를 다른 클래스의 내부에 선언할 수도 있는데 이 때 확장 선언을 포함하는 클래스의 인스턴스를 dispatch receiver, 확장 선언에 명시된 수신자의 인스턴스를 extension receiver 라고 합니다.


두 개 이상의 개념이 겹치는 영역이므로 각각의 경우에 동작하는 규칙을 주의깊게 확인해 봐야 합니다.


상황별 동작 분석

DispatchReceiver class, ExtensionReceiver class 가 각각 선언되어 있고 DispatchReceiver class 내부에 ExtensionReceiver의 확장함수 func2가 선언되어 있을 때 func2 구현 시 this 사용 방법 및 멤버 접근 방법을 세부적으로 보면 다음과 같습니다.

  • this 키워드로 ExtensionReceiver에 명시적으로 접근 가능합니다.

  • ExtensionReceiver의 private멤버에는 접근하지 못합니다.

  • this@DispatchReceiver(qualified this syntax) 로 DispatchReceiver에도 명시적으로 접근 가능합니다.

  • ExtensionReceiver와 DispatchReceiver들의 각각의 멤버들 간에 이름 충돌이 발생하지 않는다면 멤버 접근 시 한정자(this)를 생략하고 멤버 이름만으로 접근할 수 있습니다. 즉 DispatchReceiver, ExtensionReceiver class의 멤버를 각각 구분하여 접근할 필요가 없습니다.

  • 한정자(this)를 생략하고 멤버에 접근 했는데 만약 두 클래스의 멤버간 이름 충돌이 발생한다면 ExtensionReceiver의 멤버 접근으로 처리됩니다.


전체 예제

class ExtensionReceiver {

   var value1: String = "ExtensionReceiver::value1"

   var value3: String = "ExtensionReceiver::value3" // 이름 충돌이 발생하는 멤버 프로퍼티

}


class DispatchReceiver {

   var value2: String = "DispatchReceiver::value2"

   var value3: String = "DispatchReceiver::value3" // 이름 충돌이 발생하는 멤버 프로퍼티


   fun ExtensionReceiver.func2() {

       println("ExtensionReceiver.func2()")


       // DispatchReceiver 클래스 내부에 선언된 ExtensionReceiver 클래스의 확장 함수 func2는 두 개의 클래스 멤버에 일반 멤버처럼 모두 접근할 수 있습니다.

       println("access ExtensionReceiver::value1=$value1")

       println("access DispatchReceiver::value2=$value2")


       // ExtensionReceiver => this, DispatchReceiver => this@DispatchReceiver 와 같이 명시적으로 접근할 수 있습니다.

       println("this.value1=${this.value1}")

       //println("this.value2=${this.value2}") // 컴파일 에러

       println("this.value2=${this@DispatchReceiver.value2}")


       // 만약 DispatchReceiver와 ExtensionReceiver에 동일한 선언의 멤버가 있는 경우(이름 충돌 발생) ExtensionReceiver의 멤버 접근이 우선이며 DispatchReceiver의 멤버에 접근하려면

       // this@DispatchReceiver와 같이 qualified this syntax을 이용하여 접근해야 합니다.

       println("this.value3=$value3")

       println("this@DispatchReceiver.value3=${this@DispatchReceiver.value3}")

   }


   fun func1(ext: ExtensionReceiver) {

       println("DispatchReceiver::func1()")

       //func2() // 컴파일 에러. ExtensionReceiver.func2() 는 DispatchReceiver의 확장 또는 멤버가 아닙니다.

       ext.func2()

   }


   fun func3() {

       println("DispatchReceiver::func3()")

   }

}


fun main(args: Array<String>) {

   DispatchReceiver().func1(ExtensionReceiver())

}


확장 함수인 func2 자체에 대한 접근을 세부적으로 보면 다음과 같습니다.

DispatchReceiver 내부에 선언된 ExtensionReceiver의 확장 함수 func2는 DispatchReceiver 외부에서는 접근할 수 없습니다. 마찬가지로, ExtensionReceiver의 내부에서도 func2에 접근할 수 없습니다.


ExtensionReceiver, DispatchReceiver, 확장 함수와 상속

확장 함수에는 다형성이 적용되지 않습니다. 따라서 ExtensionReceiver의 상속 계층 마다 동일한 확장 함수가 선언되어 있다고 해도 생성된 객체의 인스턴스가 아닌 생성된 객체를 참조하는 변수의 타입에 따라 확장 함수가 선택됩니다. 확장 함수가 다른 클래스 내부에 선언되었더라도 확장 함수 선택은 정적으로 동작합니다.

하지만 확장 함수를 선언한 DispatchReceiver에서는 다형성이 적용됩니다. DispatchReceiver의 상속 계층에 각각 동일한 확장 함수가 선언되어 있다면 객체를 참조하는 변수의 타입이 아니라 생성된 객체의 실제 타입(클래스)에 선언된 확장 함수가 선택됩니다.

즉 클래스 내부에 멤버 함수로 선언한 확장 함수는 자신을 포함한 클래스(DispatchReceiver)의 상속에 대해서는 다형적으로 선택됩니다.



+ Recent posts