Kotlin Delegation

Implementation by Delegation

Kotlin supports the Delegation pattern, which is an alternative to inheritance, without boilerplate code.

In the example below, the Derived class implements the Base interface by delegating all of its public members to a specific object (BaseImpl).

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

The by clause in the supertype list of the Derived class indicates that b will be stored internally in the Derived object and that the compiler will generate all Base methods that forward to b. As this example shows, the by keyword provides delegation.

Let’s look at another example. Suppose there is a BaseImpl class that implements the Base interface. If the features of BaseImpl are useful and you want to temporarily extend part of BaseImpl, you do not need to redefine all properties and functions required by the Base interface. Instead, you can write a BaseImplEx class that extends only some behavior and use it temporarily.

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

Overriding works as expected: the compiler uses the overridden implementation in the delegating class instead of the implementation in the delegate object. That is, if you add the overriding function fun printMessage() { print("abc") } to Derived, the program prints abc instead of 10 when printMessage is called.

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

However, a member overridden this way is not called from members of the delegate object, which can only access its own implementations of the interface members.

In other words, members of the delegate object can access only the implementations of the interface members.

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 {
    // This property is not accessed from b's implementation of '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

The examples above show class delegation. With property delegation, access to a property can also be delegated to another class.

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
}

References