Kotlin supports delegation as a first-class language feature, both for classes and properties.
Class delegation (by)
Delegate interface implementation to another object — composition over inheritance.
interface Printable {
fun print()
}
class ConsolePrinter : Printable {
override fun print() = println("Printing to console")
}
// PrinterService delegates Printable to ConsolePrinter
class PrinterService(private val printer: Printable) : Printable by printer
val service = PrinterService(ConsolePrinter())
service.print() // "Printing to console"Property delegation
lazy
Initialized on first access, thread-safe by default.
val heavyObject: Expensive by lazy {
println("Computing...")
Expensive() // computed once, cached
}
// Thread-safety modes
val value by lazy(Lazy.ThreadSafetyMode.NONE) { compute() } // no synchronization
val value by lazy(Lazy.ThreadSafetyMode.SYNCHRONIZED) { compute() } // default
val value by lazy(Lazy.ThreadSafetyMode.PUBLICATION) { compute() } // safe but may compute multiple timesobservable
Notifies on property change.
import kotlin.properties.Delegates
var name: String by Delegates.observable("<unset>") { _, old, new ->
println("Name changed from $old to $new")
}
name = "Alice" // prints: Name changed from <unset> to Alice
name = "Bob" // prints: Name changed from Alice to Bobvetoable
Reject property changes based on a condition.
var age: Int by Delegates.vetoable(0) { _, old, new ->
new >= 0 // only accept non-negative values
}
age = 25 // accepted
age = -5 // rejected, age stays 25notNull
Late initialization for non-nullable properties (throws before set).
var value: String by Delegates.notNull()
// value.length // throws IllegalStateException
value = "hello"
println(value) // "hello"Map delegation
Use a map as a property delegate — common for JSON/dynamic data.
class User(map: Map<String, Any?>) {
val name: String by map
val age: Int by map
}
val user = User(mapOf("name" to "Alice", "age" to 30))
println(user.name) // "Alice"
println(user.age) // 30
// Mutable map for var properties
class MutableUser(map: MutableMap<String, Any?>) {
var name: String by map
var age: Int by map
}
val mutableUser = MutableUser(mutableMapOf("name" to "Bob", "age" to 25))
mutableUser.name = "Charlie" // updates the mapCustom property delegate
Implement ReadOnlyProperty or ReadWriteProperty.
import kotlin.reflect.KProperty
class Example {
// Custom read-only delegate
class UpperCase(initial: String) : ReadOnlyProperty<Any?, String> {
private var value = initial
override fun getValue(thisRef: Any?, property: KProperty<*>): String {
return value.uppercase()
}
}
// Custom read-write delegate
class Logging<T>(initial: T) : ReadWriteProperty<Any?, T> {
private var value = initial
override fun getValue(thisRef: Any?, property: KProperty<*>): T {
println("Getting ${property.name}: $value")
return value
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
println("Setting ${property.name} from ${this.value} to $value")
this.value = value
}
}
}
// Usage
val greeting by Example.UpperCase("hello") // "HELLO"
var count by Example.Logging(0) // logs on get/setDelegated properties with provideDelegate
For controlling delegate creation at the time of property registration.
class ResourceDelegate<T>(private val key: String) : ReadOnlyProperty<Any?, T> {
override fun getValue(thisRef: Any?, property: KProperty<*>): T {
// look up resource by key
@Suppress("UNCHECKED_CAST")
return loadResource(key) as T
}
}
class ResourceDelegateProvider<T>(private val key: String) : PropertyDelegateProvider<Any?, ReadOnlyProperty<Any?, T>> {
override operator fun provideDelegate(thisRef: Any?, property: KProperty<*>): ReadOnlyProperty<Any?, T> {
// called at property registration time, not on each access
checkPropertyAccess(property.name)
return ResourceDelegate(key)
}
}
val config by ResourceDelegateProvider<String>("app.config")