Delegated Properties
| Since: | Kotlin 1.0(2016) |
|---|
Delegation is a mechanism that hands off property read/write operations to another object. Use the by keyword to specify a standard delegated property or a custom delegate.
Syntax
var propertyName: Type by delegateObject
// Delegated properties from the standard library
import kotlin.properties.Delegates
var propertyName: Type by Delegates.observable(initialValue) { prop, old, new -> }
var propertyName: Type by Delegates.notNull()
Delegate Types
| Delegate | Description |
|---|---|
| by lazy { } | A lazy property that is initialized on first access. |
| Delegates.observable(initialValue) { prop, old, new -> } | Invokes a callback whenever the value changes. Use this to observe property changes. |
| Delegates.vetoable(initialValue) { prop, old, new -> } | Updates the value only when the callback returns true. Use this for validation. |
| Delegates.notNull() | Allows declaration without an initial value, but throws an exception if accessed before initialization. |
Sample Code
sample_delegated_property.kt
import kotlin.properties.Delegates
class User {
// Logs a message whenever the value changes.
var name: String by Delegates.observable("(not set)") { _, old, new ->
println("Name changed: $old → $new")
}
// Updates the value only if the new value is 0 or greater.
var age: Int by Delegates.vetoable(0) { _, _, new ->
new >= 0
}
// Requires initialization before access.
var id: Int by Delegates.notNull()
}
fun main() {
val user = User()
user.id = 1001 // Initializes the notNull property.
user.name = "Okabe Rintaro" // Callback is invoked.
user.name = "Makise Kurisu" // Callback is invoked again.
user.age = 18
println(user.age) // 18
user.age = -1 // Rejected because the value is negative.
println(user.age) // 18 (unchanged)
}
The command looks like this:
kotlinc sample_delegated_property.kt -include-runtime -d sample_delegated_property.jar java -jar sample_delegated_property.jar Name changed: (not set) → Okabe Rintaro Name changed: Okabe Rintaro → Makise Kurisu 18 18
Common Mistakes
A notNull() property throws an exception if accessed before being initialized. by lazy is only for val — it cannot be used with var.
import kotlin.properties.Delegates
class LabMember {
// NG: notNull must be initialized before access
var name: String by Delegates.notNull()
// println(name) ← throws IllegalStateException before initialization
// NG: by lazy cannot be used with var (compile error)
// var lazyProp: String by lazy { "init" }
// NG: vetoable lambda must return Boolean; returning Unit causes a compile error
// var divergence: Double by Delegates.vetoable(0.0) { _, _, new ->
// println(new) // Unit returned — compile error
// }
}
sample_delegated_property_mistakes.kt
import kotlin.properties.Delegates
class LabMember {
var name: String by Delegates.notNull() // Initialize before accessing
val lazyProp: String by lazy { // Declare with val
println("Initializing")
"lab member"
}
var divergence: Double by Delegates.vetoable(0.0) { _, _, new ->
new in 0.0..2.0 // Return a Boolean expression
}
}
fun main() {
val okabe = LabMember()
okabe.name = "Okabe Rintaro"
println(okabe.name) // Okabe Rintaro
println(okabe.lazyProp) // Initializing → lab member
println(okabe.lazyProp) // lab member (not initialized again)
okabe.divergence = 1.048596
println(okabe.divergence) // 1.048596
okabe.divergence = 3.0 // Out of range, rejected
println(okabe.divergence) // 1.048596 (unchanged)
}
The command looks like this:
kotlinc sample_delegated_property_mistakes.kt -include-runtime -d sample_delegated_property_mistakes.jar java -jar sample_delegated_property_mistakes.jar Okabe Rintaro Initializing lab member lab member 1.048596 1.048596
Details
Delegated properties let you extract property access logic into reusable classes. Simply place a delegate object after the by keyword to handle complex access behavior in a clean, concise way.
Delegates.observable is useful when you want to observe value changes, and Delegates.vetoable is convenient when you need to validate changes before they are applied. To create a custom delegate, implement the ReadWriteProperty<T, V> interface and define both getValue and setValue.
If you find any errors or copyright issues, please contact us.