Reading — step 1 of 6
Learn
Delegated properties (by keyword) let a property hand off its get/set logic to another object. The standard library ships with several useful delegates; you can also write your own.
Lazy initialization with by lazy
val database by lazy {
println("opening connection...")
connectToDatabase()
}
fun main() {
println("started")
println(database) // first access — "opening connection..." runs now
println(database) // second access — cached, no re-init
}
lazy { } returns a Lazy<T> that:
- Doesn't run the block until first access.
- Caches the result thereafter.
- Is thread-safe by default (LazyThreadSafetyMode.SYNCHRONIZED).
Perfect for expensive resources you might not need.
observable delegate
React when a property changes:
import kotlin.properties.Delegates
class User {
var name: String by Delegates.observable("") { _, old, new ->
println("name changed: \$old -> \$new")
}
}
val u = User()
u.name = "Alice" // logs: name changed: -> Alice
u.name = "Bob" // logs: name changed: Alice -> Bob
vetoable — reject changes
Like observable, but the callback can VETO the change by returning false:
class Score {
var value: Int by Delegates.vetoable(0) { _, old, new ->
new >= 0 // reject negative values
}
}
val s = Score()
s.value = 100 // ✓ accepted
s.value = -5 // ✗ rejected; value stays 100
println(s.value) // 100
notNull delegate
For properties that must be initialized before first read but can't be set in the constructor:
class Builder {
var name: String by Delegates.notNull()
}
val b = Builder()
// println(b.name) // ✗ throws — not yet initialized
b.name = "Alice"
println(b.name) // ✓ Alice
Storing properties in a Map
Useful for parsing flexible data:
class Config(map: Map<String, Any?>) {
val name: String by map
val port: Int by map
}
val c = Config(mapOf("name" to "server", "port" to 8080))
println("\${c.name}:\${c.port}")
Reads pull values from the map by property name.
Custom delegates
A property delegate is anything that implements getValue (and optionally setValue):
class UpperCase {
private var value: String = ""
operator fun getValue(thisRef: Any?, property: kotlin.reflect.KProperty<*>): String = value
operator fun setValue(thisRef: Any?, property: kotlin.reflect.KProperty<*>, newValue: String) {
value = newValue.uppercase()
}
}
class Person {
var name: String by UpperCase()
}
val p = Person()
p.name = "alice"
println(p.name) // "ALICE"
Roll your own when no built-in delegate fits.
Common mistakes
by lazyon avar— compile error. Lazy is forvalonly.lateinitis the var equivalent.- Using
lateinit varfor primitives — only works for non-null reference types. UseDelegates.notNull()for primitives. observablecallback throwing — the value still changes; the listener just errors. Be careful with side effects.- Map delegates with mismatched key names — silent failures or KeyNotFoundException. Test thoroughly.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…