Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt

Use this file to discover all available pages before exploring further.

Kotlin custom delegation is a language feature that allows a property to offload its getter and setter implementation to an external object, known as the delegate. Instead of declaring a backing field and accessor logic within the property itself, the by keyword binds the property to the delegate, instructing the compiler to route all read and write operations to specific operator functions defined on the delegate instance. When you declare a delegated property, the compiler generates a hidden reference to the delegate object and translates property accesses into method calls on that delegate.

Syntax and Compiler Translation

The basic syntax utilizes the by keyword:
class Owner {
    var delegatedProperty: String by CustomDelegate()
}
Under the hood, the Kotlin compiler translates the above declaration into the following equivalent bytecode structure:
class Owner {
    private val delegatedProperty$delegate = CustomDelegate()
    
    var delegatedProperty: String
        get() = delegatedProperty$delegate.getValue(this, this::delegatedProperty)
        set(value) {
            delegatedProperty$delegate.setValue(this, this::delegatedProperty, value)
        }
}

Required Operator Functions

For an object to serve as a custom delegate, it must implement specific operator functions. The required functions depend on whether the property is read-only (val) or mutable (var).

1. getValue (Required for val and var)

This function intercepts read operations. It requires two parameters:
  • thisRef: The reference to the object that owns the property. The type of this parameter restricts where the delegate can be applied. Using Any? allows the delegate to be used anywhere.
  • property: Reflection metadata about the property being accessed, represented by KProperty<*> or its supertypes.

2. setValue (Required for var only)

This function intercepts write operations. It requires three parameters:
  • thisRef: Same as in getValue.
  • property: Same as in getValue.
  • value: The new value being assigned. The type must match or be a supertype of the property’s type.

Implementation Approaches

There are three primary ways to implement a custom delegate in Kotlin.

Approach 1: Manual Operator Overloading

You can define the getValue and setValue functions directly on a class using the operator modifier.
import kotlin.reflect.KProperty

class CustomDelegate {
    private var backingValue: String = "Default"

    operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
        return backingValue
    }

    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
        backingValue = value
    }
}

Approach 2: Standard Library Interfaces

Kotlin provides two generic interfaces in the kotlin.properties package to enforce the correct signatures for delegates: ReadOnlyProperty<T, V> and ReadWriteProperty<T, V>.
  • T represents the type of thisRef (the owner).
  • V represents the type of the property.
Using these interfaces provides compile-time safety and eliminates the need to manually write the operator keyword.
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty

class TypedDelegate : ReadWriteProperty<Owner, String> {
    private var backingValue: String = "Initial"

    override fun getValue(thisRef: Owner, property: KProperty<*>): String {
        return backingValue
    }

    override fun setValue(thisRef: Owner, property: KProperty<*>, value: String) {
        backingValue = value
    }
}

Approach 3: Extension Functions

Delegation operator functions do not need to be member functions; they can be provided as extension functions. This allows you to use existing classes as delegates even if they were not originally designed for it.
import kotlin.reflect.KProperty

class ExternalStorage {
    fun retrieve(key: String): Int = 0
    fun store(key: String, value: Int) { }
}

// Providing delegate operators via extensions
operator fun ExternalStorage.getValue(thisRef: Any?, property: KProperty<*>): Int {
    return this.retrieve(property.name)
}

operator fun ExternalStorage.setValue(thisRef: Any?, property: KProperty<*>, value: Int) {
    this.store(property.name, value)
}

class Consumer {
    // ExternalStorage can now be used as a delegate
    var count: Int by ExternalStorage()
}

Property Delegation Providers

If you need to execute logic during the creation of the delegate (before the property is accessed), Kotlin supports the provideDelegate operator. This operator intercepts the binding of the delegate to the property, allowing you to inspect the property metadata or return a different delegate instance based on the context.
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty

class DelegateFactory {
    operator fun provideDelegate(
        thisRef: Any?, 
        property: KProperty<*>
    ): ReadOnlyProperty<Any?, String> {
        // Initialization logic here (e.g., checking property.name)
        return ReadOnlyProperty { _, _ -> "Provided Value" }
    }
}

class Target {
    val property: String by DelegateFactory()
}
Master Kotlin with Deep Grasping Methodology!Learn More