Sobes.tech
Middle

Explain how delegation works in Kotlin.

sobes.tech AI

Answer from AI

Delegation in Kotlin is a mechanism that allows an object to delegate some of its responsibilities to another object. This is implemented without inheritance, which promotes more flexible composition.

It is used for:

  • Reusing functionality.
  • Implementing the Delegation pattern.

Kotlin supports delegation at the class level and property level.

At the class level:

A class can delegate the implementation of an interface to another object using the by operator. This eliminates the need to manually override all interface methods.

// Interface
interface Base {
    fun printMessage()
    fun printMessageLine()
}

// Delegate class
class BaseImpl(val x: Int) : Base {
    override fun printMessage() {
        print(x)
    }

    override fun printMessageLine() {
        println(x)
    }
}

// Class delegating implementation
class Derived(b: Base) : Base by b

At the property level:

The value of a property can be computed through a delegate. This allows implementing patterns such as lazy initialization, observable properties, and properties with thread-local storage.

Syntax: val <propertyName>: <PropertyType> by <delegateExpression>.

Examples of built-in property delegates:

  • lazy: For lazy initialization. The value is computed only upon the first access.
    val lazyValue: String by lazy {
        println("Computing lazyValue")
        "Hello"
    }
    
    fun main() {
        println(lazyValue) // "Computing lazyValue" will be printed here
        println(lazyValue) // Second call will not recompute
    }
    
  • Delegates.observable: Calls a specified block of code when the property value changes.
    import kotlin.properties.Delegates
    
    var name: String by Delegates.observable("<no name>") {
        prop, old, new ->
        println("$old -> $new")
    }
    
    fun main() {
        name = "Alice" // <no name> -> Alice
        name = "Bob"   // Alice -> Bob
    }
    
  • Delegates.vetoable: Allows canceling the assignment of a new value if a condition is not met.
    import kotlin.properties.Delegates
    
    var max: Int by Delegates.vetoable(0) {
        prop, old, new -> new > old
    }
    
    fun main() {
        println(max) // 0
        max = 10
        println(max) // 10
        max = 5    // Will not change because 5 ≤ 10
        println(max) // 10
    }
    

Creating custom property delegates:

Implemented by the interfaces ReadOnlyProperty or ReadWriteProperty.

  • ReadOnlyProperty: For val properties. Implements getValue.
  • ReadWriteProperty: For var properties. Implements getValue and setValue.
import kotlin.reflect.KProperty

class ExampleDelegate {
    var value: String = "Initial value"

    operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
        println("$thisRef, thank you for delegating '${property.name}' to me!")
        return value
    }

    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
        println("$thisRef, thank you for notifying about assigning '${property.name}' to new value '$value'")
        this.value = value
    }
}

class Container {
    var myProp: String by ExampleDelegate()
}

fun main() {
    val c = Container()
    println(c.myProp) // Accesses getValue
    c.myProp = "New value" // Accesses setValue
    println(c.myProp)
}

Delegation ensures code cleanliness, improves readability, and promotes the DRY (Don't Repeat Yourself) principle.

Explain how delegation works in Kotlin. — Kotlin - sobes.tech