Skip to main content

Delegates and DSLs

Property delegation​

A delegate handles property reads/writes.

val configuration by lazy {
println("Loading")
"Production"
}

The initializer runs once, on first access.

class User { var name: String by Delegates.observable("Unknown") { _, old, new ->
println("$old -> $new")
} }
Open in Kotlin Playground

Class delegation​

interface Logger { fun log(message: String) }
class ConsoleLogger : Logger { override fun log(message: String) = println(message) }
class Repository(logger: Logger) : Logger by logger

Repository forwards Logger members to logger without hand-written boilerplate.

DSL with receiver lambdas​

class RequestBuilder {
var url: String = ""
val headers = mutableMapOf<String, String>()
}

fun request(block: RequestBuilder.() -> Unit): RequestBuilder =
RequestBuilder().apply(block)
val request = request {
url = "https://example.com"
headers["Accept"] = "application/json"
}

DSLs are useful when the vocabulary is small and the nesting is clear. Use @DslMarker to prevent accidentally accessing the wrong receiver in nested builders.

✕ DSL hides required values

val request = request { } // empty URL accepted until much later

✓ Require essential values in the function signature

fun request(url: String, block: RequestBuilder.() -> Unit = {}) =
RequestBuilder().apply { this.url = url }.apply(block)
Interview-ready answer

Property delegates reuse get/set behavior, class delegation forwards an interface to another object, and DSLs use receiver lambdas to create a focused language. DSL syntax should not hide validation or required dependencies.