Kotlin 위임 (Delegation)
위임 구현 (Implementation by Delegation)
kotlin 은 상속의 대안이 되는 Delegation pattern을 boilerplate code 없이 지원한다.
아래 예제에서, Derived
클래스는 자신의 모든 public
멤버들을 특정 객체(BaseImpl
)로 위임하여 Base
인터페이스를 구현하고 있다.
interface Base {
fun print()
}
class BaseImpl(val x: Int) : Base {
override fun print() {
print(x)
}
}
class Derived(b: Base) : Base by b
fun main() {
val b = BaseImpl(10)
Derived(b).print()
}
Output:
10
Derived
클래스의 상위 타입 목록 안에 쓰인 by
는 b
를 Derived
의 객체 안에 내부적으로 저장될 것이고, 컴파일러는 b
로 보내는 Base
의 모든 메소드들을 생성할 것임을 나타낸다. 이 예제에서 알 수 있듯이 키워드 by
은 위임하는 기능을 제공한다.
또 다른 예를 보도록 하자. 아래 예제에서는 Base
인터페이스를 구현하는 BaseImpl
클래스가 있다고 하자. BaseImpl
클래스의 기능들이 좋아사 BaseImpl
의 일부를 일시적으로 확장 기능을 이용하고 싶은 경우에 Base
인터페이스가 요구하는 속성과 함수를 모두 다시 정의하는 것이 아니라 일부 기능만을 확장한 BaseImplEx
클래스를 작성하여 일시적으로 사용할 수 있다.
interface Base {
fun funcA()
fun funcB()
}
class BaseImpl : Base {
override fun funcA() {
println("AAA")
}
override fun funcB() {
println("BBB")
}
}
class BaseImplEx(b: Base) : Base by b {
override fun funcA() {
println("AAA!!!")
}
}
fun main() {
println("== BaseImpl ====")
val b = BaseImpl()
b.funcA()
b.funcB()
println("== BaseImplEx ====")
val bx = BaseImplEx(b)
bx.funcA()
bx.funcB()
}
Output:
== BaseImpl ====
AAA
BBB
== BaseImplEx ====
AAA!!!
BBB
위임에 의해 구현된 인터페이스의 멤버 재정의 (Overriding a member of an interface implemented by delegation)
재정의는 예상대로 작동한다: 컴파일러는 위임 객체의 재정의된 구현체 보다는 다시 재정의된 구현체를 사용한다. 즉, Derived에 재정의 함수 fun printMessage() { print("abc") }
를 추가하였다면 프로그램은 printMessage
가 호출되면 10
대신에 abc
를 출력하게 된다.
interface Base {
fun printMessage()
fun printMessageLine()
}
class BaseImpl(val x: Int) : Base {
override fun printMessage() { print(x) }
override fun printMessageLine() { println(x) }
}
class Derived(b: Base) : Base by b {
override fun printMessage() { print("abc") }
}
fun main() {
val b = BaseImpl(10)
Derived(b).printMessage()
Derived(b).printMessageLine()
}
Output:
abc10
그러나 이러한 방식으로 재정의된 멤버는 인터페이스 멤버의 자체 구현에만 액세스할 수 있는 위임 객체의 멤버에서 호출되지 않는다.
즉, 위임 객체의 멤버들은 인터페이스 멤버들의 구현들만 접근이 가능하다.
interface Base {
val message: String
fun print()
}
class BaseImpl(x: Int) : Base {
override val message = "BaseImpl: x = $x"
override fun print() { println(message) }
}
class Derived(b: Base) : Base by b {
// 이 속성은 b의 'print()' 구현에서 액세스할 수 없다.
override val message = "Message of Derived"
}
fun main() {
val b = BaseImpl(10)
val derived = Derived(b)
derived.print()
println(derived.message)
}
Output:
BaseImpl: x = 10
Message of Derived
위임 속성 (Delegated Properties)
위 예제는 Class Delegation 이고, Property Delegation 으로 속성에 대한 액세스를 다른 클래스에 위임 할 수도 있다.
import kotlin.reflect.KProperty
class Example {
var p: String by Delegate()
override fun toString() = "Example"
}
class Delegate() {
operator fun getValue(thisRef: Any?, prop: KProperty<*>): String {
return "-- Get ${prop.name} from $thisRef"
}
operator fun setValue(thisRef: Any?, prop: KProperty<*>, value: String) {
println("-- Set \"$value\" to ${prop.name} in $thisRef")
}
}
fun main() {
val e = Example()
e.p = "NEW STRING" // -- Set "NEW STRING" to p in Example
println(e.p) // -- Get p from Example
}