Delegated Properties in Kotlin
Delegated Properties in Kotlin
Kotlin delegated properties allow a class to hand off the getter and setter logic of a property to a separate delegate object using the by keyword. The compiler translates property access into calls on the delegate's getValue() and setValue() operators, which means the storage, validation, or computation logic can be extracted and reused across unrelated classes. By the end of this lesson, you will be able to:
- Explain how the Kotlin compiler transforms a delegated property into calls to
getValue()andsetValue(). - Describe the behavior and thread safety of the
lazydelegate. - Use
Delegates.observableandDelegates.vetoablefor change tracking and validation. - Implement a map backed delegate for dynamic property resolution.
- Build a custom delegate by implementing the
ReadOnlyPropertyorReadWritePropertyinterface.
Compiler Transformation of Delegated Properties
When you declare a delegated property, the compiler generates a hidden field that holds the delegate object and rewrites every property access to call through it:
class Example {
var text: String by SomeDelegate()
}
The compiler transforms this into roughly:
class Example {
private val text$delegate = SomeDelegate()
var text: String
get() = text$delegate.getValue(this, ::text)
set(value) {
text$delegate.setValue(this, ::text, value)
}
}
The delegate must provide getValue() (and setValue() for mutable properties) with specific signatures. The first parameter is the property owner (thisRef), and the second is a KProperty<*> representing the property metadata. The Kotlin standard library provides the ReadOnlyProperty and ReadWriteProperty interfaces to formalize this contract.
Lazy Initialization
The lazy delegate delays computation until the first property access and caches the result for subsequent reads. It takes a lambda that produces the value:
val expensiveResult: String by lazy {
println("Computing...")
buildExpensiveString()
}
The first access executes the lambda, stores the result, and returns it. Every subsequent access returns the cached value without re executing the lambda. By default, lazy uses LazyThreadSafetyMode.SYNCHRONIZED, which means it is safe to access from multiple threads but imposes the cost of synchronization on every read until initialization completes.
Two other modes are available. LazyThreadSafetyMode.PUBLICATION allows multiple threads to compute the value simultaneously but only stores the first result. LazyThreadSafetyMode.NONE skips all synchronization and is appropriate when you guarantee single threaded access, such as properties accessed only on the main thread in an Android application:
This interview continues for subscribers
Subscribe to Dove Letter for full access to exclusive interviews about Android and Kotlin development.
Become a Sponsor