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.

Property delegation is a language feature in Kotlin that transfers the responsibility of implementing a property’s read and write behavior (the getter and setter) to a separate object, known as the delegate. Instead of declaring a backing field and accessor logic directly within the property, the compiler routes all property accesses to the delegate instance. The syntax relies on the by keyword:
val/var <propertyName>: <Type> by <delegateExpression>

Compiler Mechanics

When a property is delegated, the Kotlin compiler does not generate a standard backing field. Instead, it evaluates the <delegateExpression>, stores it in a hidden, compiler-generated reference (e.g., `p$delegate`), and synthesizes the property’s getter and setter to invoke specific operator functions on that hidden reference. For a property declared as var p: String by Delegate(), the compiler translates property access roughly as follows:
import kotlin.reflect.KProperty

class Delegate {
    operator fun getValue(thisRef: Any?, property: KProperty<*>): String = "Value"
    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {}
}

class Example {
    // Conceptual representation of compiler-generated code:
    private val `p$delegate` = Delegate()

    var p: String
        get() = `p$delegate`.getValue(this, this::p)
        set(value) { `p$delegate`.setValue(this, this::p, value) }
}

The Delegate Contract

To act as a delegate, an object must adhere to a specific contract by providing getValue and (for mutable properties) setValue operator functions. These can be implemented as member functions or extension functions.

Read-Only Properties (val)

For a val property, the delegate must provide a getValue function with the following signature:
operator fun getValue(thisRef: T, property: KProperty<*>): V

Mutable Properties (var)

For a var property, the delegate must provide both getValue and setValue functions. The getValue signature is identical to the read-only contract, while setValue adds a parameter for the assigned value:
operator fun getValue(thisRef: T, property: KProperty<*>): V
operator fun setValue(thisRef: T, property: KProperty<*>, value: V)
Parameter Breakdown:
  • thisRef: Represents the object hosting the property. Its type T dictates the context in which the delegate can be used (e.g., restricting a delegate to only be usable within a specific class).
  • property: A reflection object of type KProperty<*> containing metadata about the delegated property, such as its name.
  • value: The new value being assigned to the property. Its type V must be the same as, or a supertype of, the property type.

Implementation via Standard Interfaces

While you can write the operator functions manually, the Kotlin standard library provides the ReadOnlyProperty and ReadWriteProperty interfaces to enforce the delegate contract with compile-time type safety.
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty

class CustomDelegate : ReadWriteProperty<Any?, String> {
    private var internalState: String = "Initial"

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

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

class DataEntity {
    var data: String by CustomDelegate()
}

Standard Library Delegates

The Kotlin standard library provides built-in factory methods for common delegation patterns:
import kotlin.properties.Delegates

class StandardDelegatesExample {
    // lazy: The lambda is executed on the first access and the result is stored.
    val lazyValue: String by lazy {
        "Computed once"
    }

    // observable: Invokes the callback after the property's value changes.
    var name: String by Delegates.observable("Initial") { property, oldValue, newValue ->
        println("${property.name} changed from $oldValue to $newValue")
    }

    // vetoable: Invokes the callback before the value changes. 
    // If it returns false, the assignment is intercepted and discarded.
    var age: Int by Delegates.vetoable(0) { property, oldValue, newValue ->
        newValue >= 0 
    }
}

Delegating to a Map

Properties can be delegated directly to a Map (for read-only val properties) or a MutableMap (for read-write var properties). The Kotlin standard library provides extension functions on Map and MutableMap that satisfy the delegate contract. The compiler uses the property’s name as the string key to retrieve or update the corresponding value in the map.
class User(val map: Map<String, Any?>) {
    val name: String by map
    val age: Int     by map
}

class MutableUser(val map: MutableMap<String, Any?>) {
    var name: String by map
    var age: Int     by map
}

Local Delegated Properties

The delegation mechanism is not restricted to class-level properties; it can also be applied to local variables within functions. The delegate expression is evaluated at the point of variable declaration.
fun processData(condition: Boolean, computeHeavyData: () -> String) {
    val localLazyValue: String by lazy { computeHeavyData() }
    
    if (condition) {
        // computeHeavyData() is only executed if this branch is reached
        println(localLazyValue) 
    }
}

Property Binding Interception (provideDelegate)

Kotlin provides an advanced hook into the delegation lifecycle via the provideDelegate operator. If the object on the right side of the by keyword defines provideDelegate, the compiler will invoke it exactly once during the creation of the property, before the hidden delegate instance is bound. This mechanism is used to inspect property metadata or perform initialization logic at the moment the property is instantiated, rather than at the time of first access.
class DelegateFactory {
    operator fun provideDelegate(
        thisRef: Any?,
        property: KProperty<*>
    ): ReadWriteProperty<Any?, String> {
        // Executed during property initialization
        check(property.name.isNotEmpty()) { "Property name cannot be empty" }
        
        // Must return the actual delegate instance
        return CustomDelegate() 
    }
}

class ConfigEntity {
    // provideDelegate is called immediately upon ConfigEntity instantiation
    var data: String by DelegateFactory() 
}
Master Kotlin with Deep Grasping Methodology!Learn More