Easy Tutorial
❮ Home Kotlin Eclipse Setup ❯

Delegate Pattern in Kotlin

The Delegation Pattern is a fundamental technique in software design patterns. In the delegation pattern, two objects are involved in handling the same request, and the object that receives the request delegates it to another object for processing.

Kotlin directly supports the delegation pattern in a more elegant and concise manner. Kotlin implements delegation through the keyword 'by'.


Class Delegation

Class delegation means that the methods defined in a class are actually implemented by calling the methods of another class's object.

In the following example, the derived class Derived inherits all the methods of the interface Base and delegates the execution of these methods to an input Base class object.

// Create an interface
interface Base {   
    fun print()
}

// The class that implements this interface is the delegated class
class BaseImpl(val x: Int) : Base {
    override fun print() { print(x) }
}

// Establish a delegated class with the keyword 'by'
class Derived(b: Base) : Base by b

fun main(args: Array<String>) {
    val b = BaseImpl(10)
    Derived(b).print() // Output 10
}

In the declaration of Derived, the 'by' clause indicates that b is saved inside the object instance of Derived, and the compiler will generate all methods inherited from the Base interface, forwarding the calls to b.


Property Delegation

Property delegation refers to a class's property value not being defined directly within the class, but instead being entrusted to a delegate class to achieve unified management of the class's properties.

Property delegation syntax format:

val/var &lt;property name>: <type> by <expression>

The expression following the 'by' keyword is the delegation. The property's get() method (and set() method for var properties) will be delegated to the getValue() and setValue() methods of this object. Property delegation does not need to implement any interfaces, but it must provide a getValue() function (and for var properties, a setValue() function is also required).

Define a Delegated Class

This class needs to include getValue() and setValue() methods, with the parameter thisRef as the object of the class being delegated, and prop as the object of the property being delegated.

import kotlin.reflect.KProperty
// Define a class with property delegation
class Example {
    var p: String by Delegate()
}

// The delegate class
class Delegate {
    operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
        return "$thisRef, the ${property.name} property is delegated here"
    }

    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
        println("The ${property.name} property of $thisRef is assigned the value of $value")
    }
}
fun main(args: Array<String>) {
    val e = Example()
    println(e.p)     // Access this property, call the getValue() function

    e.p = "tutorialpro"   // Call the setValue() function
    println(e.p)
}

Output results:

Example@433c675d, the p property is delegated here
The p property of Example@433c675d is assigned the value of tutorialpro
Example@433c675d, the p property is delegated here

Standard Delegation

Kotlin's standard library already includes many factory methods to implement property delegation.

Lazy Property

lazy() is a function that accepts a Lambda expression as a parameter and returns a Lazy<T> instance function, which can be used as a delegate for implementing lazy properties: The first call to get() will execute the lamda expression passed to lazy() and record the result, and subsequent calls to get() will only return the recorded result.

val lazyValue: String by lazy {
    println("computed!")     // Output on the first call, not executed on the second call
    "Hello"
}

fun main(args: Array<String>) {
    println(lazyValue)   // First execution, execute the expression twice
    println(lazyValue)   // Second execution, only output the return value
}

Execution output results:

computed!
Hello
Hello

Observable Properties

observable can be used to implement the observer pattern.

The Delegates.observable() function accepts two parameters: the first is the initial value, and the second is the handler for the property value change event.

After the property is assigned, the event handler will be executed, which has three parameters: the property being assigned, the old value, and the new value:

import kotlin.properties.Delegate
By defining the `provideDelegate` operator, you can extend the creation of property delegation to implement the logic of the object being delegated. If the object used on the right side of `by` defines `provideDelegate` as a member or extension function, that function will be called to create the property delegate instance.

A possible use case for `provideDelegate` is to check property consistency when creating properties (not just in their getter or setter).

For example, if you want to check the property name before binding, you can write it like this:

class ResourceLoader<T>(id: ResourceID<T>) { operator fun provideDelegate( thisRef: MyUI, prop: KProperty<*> ): ReadOnlyProperty<MyUI, T> { checkProperty(thisRef, prop.name) // Create the delegate }

private fun checkProperty(thisRef: MyUI, name: String) { …… }

}

fun <T> bindResource(id: ResourceID<T>): ResourceLoader<T> { …… }

class MyUI { val image by bindResource(ResourceID.image_id) val text by bindResource(ResourceID.text_id) }


The parameters of `provideDelegate` are the same as `getValue`:

- `thisRef` —— Must be the same as the type of the property owner (for extension properties — the type being extended) or its supertype.

- `property` —— Must be of type `KProperty<*>` or its supertype.

During the creation of a `MyUI` instance, the `provideDelegate` method is called for each property, and the necessary validation is performed immediately.

Without this ability to intercept the binding between the property and its delegate, to achieve the same functionality, you would have to explicitly pass the property name, which is not very convenient:

// Check the property name without using the "provideDelegate" feature class MyUI { val image by bindResource(ResourceID.image_id, "image") val text by bindResource(ResourceID.text_id, "text") }

fun <T> MyUI.bindResource( id: ResourceID<T>, propertyName: String ): ReadOnlyProperty<MyUI, T> { checkProperty(this, propertyName) // Create the delegate }


In the generated code, the `provideDelegate` method is called to initialize the auxiliary `prop$delegate` property. Compare the code generated for the property declaration `val prop: Type by MyDelegate()` with the code above (when the `provideDelegate` method does not exist):

class C { var prop: Type by MyDelegate() }

// This code is generated by the compiler when the "provideDelegate" feature is available: class C { // Call "provideDelegate" to create an additional "delegate" property private val prop$delegate = MyDelegate().provideDelegate(this, this::prop) val prop: Type get() = prop$delegate.getValue(this, this::prop) } ```

Please note that the provideDelegate method only affects the creation of the auxiliary property and does not affect the code generated for the getter or setter.

❮ Home Kotlin Eclipse Setup ❯