Property Delegation in Kotlin is a feature that allows a property to delegate its getter and setter
logic to another object. This mechanism is achieved using the by keyword and helps in
reducing boilerplate code, improving reusability, and adding flexibility to how properties behave.
Normally, when you define a property in a class, you manage its value storage and retrieval directly. However, with delegated properties, you assign these responsibilities to a delegate object, which handles them for you.
class Example {
var name: String by Delegate()
}
Here, the class Delegate defines how the property name is read and written.
The lazy delegate is used when you want to initialize a property only once and only when it is
first accessed (also known as lazy initialization). This is especially useful for expensive
computations or objects that might not always be needed.
class UserProfile {
val userData: String by lazy {
println("Loading user data...")
"User: Talha"
}
}
fun main() {
val profile = UserProfile()
println(profile.userData) // Initializes and prints the value
println(profile.userData) // Uses cached value, does not reinitialize
}
Output:
Loading user data...
User: Talha
User: Talha
lazy is thread-safe by default in Kotlin.
The observable delegate is used when you want to monitor changes to a property.
It takes an initial value and a lambda that is called every time the property changes.
import kotlin.properties.Delegates
class Settings {
var theme: String by Delegates.observable("Light") { property, oldValue, newValue ->
println("Theme changed from $oldValue to $newValue")
}
}
fun main() {
val settings = Settings()
settings.theme = "Dark"
settings.theme = "Blue"
}
Output:
Theme changed from Light to Dark
Theme changed from Dark to Blue
The vetoable delegate works similarly to observable, but it allows you
to decide whether to accept or reject a new value before it is assigned.
import kotlin.properties.Delegates
class BankAccount {
var balance: Int by Delegates.vetoable(0) { _, oldValue, newValue ->
if (newValue >= 0) {
println("Balance updated from $oldValue to $newValue")
true // Accept new value
} else {
println("Invalid balance! Cannot assign negative value.")
false // Reject new value
}
}
}
fun main() {
val account = BankAccount()
account.balance = 500
account.balance = -100
}
Output:
Balance updated from 0 to 500
Invalid balance! Cannot assign negative value.
You can also create your own custom delegate by defining an object with
getValue and setValue operator functions.
import kotlin.reflect.KProperty
class StringDelegate {
private var storedValue: String = "Default"
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
println("Getting value of '${property.name}'")
return storedValue
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
println("Setting '${property.name}' to '$value'")
storedValue = value
}
}
class Example {
var text: String by StringDelegate()
}
fun main() {
val ex = Example()
ex.text = "Hello Kotlin"
println(ex.text)
}
| Delegate Type | Purpose | When Executed | Example Use Case |
|---|---|---|---|
| lazy | Initializes a property only once, on first access. | At first access. | Loading user profile data, configuration, or heavy objects. |
| observable | Observes property value changes. | Every time value changes. | Updating UI or preferences. |
| vetoable | Validates and decides whether to accept a new value. | Before value assignment. | Input validation or enforcing constraints. |
lazy initializes properties only once on first access.observable triggers a callback after every change.vetoable validates and can block undesired assignments.In essence: Property Delegation in Kotlin enhances flexibility, reduces boilerplate code, and provides elegant ways to manage state, validation, and initialization.