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.

A delegated property in Kotlin is a property whose getter and setter logic is abstracted and routed to a separate object, known as the delegate. Instead of implementing the backing field and accessors directly within the property declaration, the Kotlin compiler delegates read and write operations to specific operator functions defined on the delegate instance.

Syntax

The delegation is established using the by keyword:
val/var <property name>: <Type> by <delegate>
This syntax applies to member properties, top-level properties, extension properties, and local delegated properties (variables declared inside functions or blocks).

Compiler Mechanics

When you declare a delegated property, the Kotlin compiler generates a hidden backing field that holds the instance of the delegate. It then translates all property accesses into method calls on that delegate:
  • Reading the property translates to a call to the getValue() operator.
  • Writing to the property (if it is a var) translates to a call to the setValue() operator.

Operator Function Signatures

To act as a delegate, an object must provide specific operator functions. These functions can be implemented directly as member functions or as extension functions. For a read-only property (val), the delegate must provide a getValue operator:
import kotlin.reflect.KProperty

class StringDelegate {
    operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
        return "Delegated Value"
    }
}
For a mutable property (var), the delegate must provide both getValue and setValue operators:
import kotlin.reflect.KProperty

class MutableStringDelegate {
    private var backingValue: String = "Initial"

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

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

Parameter Breakdown

  • thisRef: Represents the receiver of the property. Its type must be the same as, or a supertype of, the expected receiver.
    • For a member property, it is the object in which the property is declared.
    • For an extension property (whether top-level or member), it is the extension receiver.
    • For a top-level non-extension property or a local delegated variable, it is Nothing?.
  • property: Holds metadata about the property being accessed, such as its name. It must be of type KProperty<*> or a supertype.
  • value (Setter only): The new value being assigned. Its type must be the exact type of the property or its supertype.

Standard Library Interfaces

Instead of writing the operator functions from scratch, developers can implement the ReadOnlyProperty or ReadWriteProperty interfaces provided by the Kotlin standard library. This ensures type safety and correct method signatures at compile time.
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty

class TypedDelegate : ReadWriteProperty<Any?, String> {
    override fun getValue(thisRef: Any?, property: KProperty<*>): String {
        return "Value"
    }

    override fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
        // Assignment logic
    }
}

Built-in Standard Library Delegates

The Kotlin standard library provides several factory methods and implementations for common delegation mechanics:

lazy

The lazy() function returns a Lazy<T> instance that serves as a delegate for implementing a read-only property. The first call to getValue() executes the lambda passed to lazy() and records the result. Subsequent calls return the recorded result without re-executing the lambda.
val lazyValue: String by lazy {
    "Computed Value"
}

Delegates.observable

Returns a ReadWriteProperty that intercepts assignments. The delegate takes an initial value and a callback lambda. The callback is invoked after the assignment to the backing field completes, receiving the property metadata, the old value, and the new value.
import kotlin.properties.Delegates

var observableValue: String by Delegates.observable("Initial") { property, old, new ->
    // Executes after modification
}

Delegates.vetoable

Returns a ReadWriteProperty that intercepts assignments before they are applied to the backing field. If the callback lambda returns true, the assignment proceeds. If it returns false, the new value is discarded, and the property retains its old value.
import kotlin.properties.Delegates

var vetoableValue: Int by Delegates.vetoable(0) { property, old, new ->
    new >= 0 // Rejects negative assignments
}

Map Delegation

Properties can be delegated directly to a Map (for read-only val properties) or a MutableMap (for mutable var properties). The delegate uses the property’s name (via the KProperty metadata) 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
}

The provideDelegate Operator

Kotlin supports an optional provideDelegate operator. If the object on the right side of the by keyword defines this operator, the compiler calls it to create the actual delegate instance during the property’s initialization. This allows for validation or custom logic to be executed at the exact moment the property is bound to the delegate, rather than waiting for the first read/write operation.
import kotlin.reflect.KProperty

class DelegateProvider {
    operator fun provideDelegate(thisRef: Any?, property: KProperty<*>): StringDelegate {
        return StringDelegate()
    }
}

class Example {
    val myProperty: String by DelegateProvider()
}
Master Kotlin with Deep Grasping Methodology!Learn More