Interview QuestionPractical QuestionFollow-up Questions

Delegated Properties in Kotlin

skydovesJaewoong Eum (skydoves)||8 min read

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() and setValue().
  • Describe the behavior and thread safety of the lazy delegate.
  • Use Delegates.observable and Delegates.vetoable for change tracking and validation.
  • Implement a map backed delegate for dynamic property resolution.
  • Build a custom delegate by implementing the ReadOnlyProperty or ReadWriteProperty interface.

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