You are currently viewing Kotlin Standard Delegates

Kotlin Standard Delegates

Standard delegates are powerful features provided by the language to simplify property management. The main standard delegates are lazy, observable, and vetoable and notNull. Each serves a distinct purpose, helping manage property initialization, changes, and validations more effectively. Let’s explore these with examples and explanations.

1. Lazy Delegation

The lazy delegate is used for lazy initialization. The property value is computed only once when it is accessed for the first time. The property is not initialized when the object is created. Instead, it is initialized the first time it is accessed.

This can save time and memory, especially if the property is expensive to create or might never be used. By default, lazy is thread-safe, meaning it ensures the property is initialized only once, even if accessed from multiple threads.

val myValue: String by lazy {
    println("Computed!")
    "Hello, World!"
}

Explanation:

  • Initialization: The block of code inside lazy is executed only once, the first time myValue is accessed.
  • Thread Safety: By default, lazy is thread-safe and uses a synchronized block to ensure only one thread initializes the value. There are different modes for thread safety like LazyThreadSafetyMode.PUBLICATION and LazyThreadSafetyMode.NONE.

Usage:

fun main() {
    println(myValue)  // Prints "Computed!" followed by "Hello, World!"
    println(myValue)  // Prints "Hello, World!" (value is not recomputed)
}
Output
Computed!
Hello, World!
Hello, World!

2. Observable Delegation

The observable delegate allows you to react to changes in a property. It takes an initial value and a callback that gets invoked each time the property is assigned a new value.

Change Tracking: It allows you to execute a block of code whenever the property value changes. This is useful for debugging, logging, or updating the UI in response to property changes.

Automatic Notification: It Simplifies the process of reacting to property changes without manually adding boilerplate code.

import kotlin.properties.Delegates

var myObservableProperty: String by Delegates.observable("Initial Value") { property, oldValue, newValue ->
    println("Property '${property.name}' changed from '$oldValue' to '$newValue'")
}
Explanation:
  • Initialization: The property starts with the initial value “Initial Value”.
  • Callback: The lambda function is called every time the property is updated, allowing you to react to the change.

Usage:

fun main() {
    myObservableProperty = "New Value"  // Triggers the callback
    myObservableProperty = "Another Value"  // Triggers the callback again
}

Output:

Property 'myObservableProperty' changed from 'Initial Value' to 'New Value'
Property 'myObservableProperty' changed from 'New Value' to 'Another Value'

3. Vetoable Delegation

The vetoable delegate allows you to veto changes to a property. It takes an initial value and a callback that determines whether the new value should be accepted or not.

  • Validation: Allows you to enforce constraints on property values before they are set. This helps maintain the integrity of the data and prevents invalid values from being assigned.
  • Custom Logic: Provides a mechanism to include custom validation logic in property assignments.
import kotlin.properties.Delegates

var myVetoableProperty: Int by Delegates.vetoable(0) { property, oldValue, newValue ->
    newValue >= 0  // Only accept non-negative values
}

Explanation:

  • Initialization: The property starts with the initial value 0.
  • Callback: The lambda function is called each time the property is updated. If the function returns true, the new value is accepted; otherwise, it is rejected.
Usage:
fun main() {
    myVetoableProperty = 10
    println(myVetoableProperty)  // Prints: 10

    myVetoableProperty = -5
    println(myVetoableProperty)  // Prints: 10 (change is vetoed)
}

4. NotNull Delegation

The notNull delegate allows you to declare a property that should not be null and throws an exception if accessed before being initialized. This is useful for properties that are supposed to be initialized later, for example, in dependency injection or in init blocks.

import kotlin.properties.Delegates

var myNotNullProperty: String by Delegates.notNull<String>()

Explanation:

  • Non-null Property: Ensures the property cannot be accessed before being initialized.
  • Exception on Access: Throws an IllegalStateException if accessed before initialization.
fun main() {
    try {
        println(myNotNullProperty)  // Throws an exception
    } catch (e: IllegalStateException) {
        println("Caught exception: ${e.message}")
    }

    myNotNullProperty = "Initialized Value"
    println(myNotNullProperty)  // Prints: Initialized Value
}
Output:
Caught exception: Property myNotNullProperty should be initialized before get.
Initialized Value

These delegates can significantly reduce boilerplate code and improve readability and maintainability in your Kotlin applications.

Also Read :