DEV Community

myougaTheAxo
myougaTheAxo

Posted on

Kotlin DSL & Builder Patterns — apply, buildList & Custom DSLs

Kotlin DSLs & Builder Patterns

Kotlin's scope functions and receiver lambdas enable elegant DSL patterns. Learn apply, buildList, and create custom DSLs.

apply for Object Initialization

The apply function returns the object after configuring it:

val user = User("Alice").apply {
    age = 30
    email = "alice@example.com"
    isActive = true
}
Enter fullscreen mode Exit fullscreen mode

Much cleaner than:

val user = User("Alice")
user.age = 30
user.email = "alice@example.com"
Enter fullscreen mode Exit fullscreen mode

buildList & buildMap

Create collections with conditional items:

val features = buildList {
    add("Login")
    add("Profile")
    if (isAdmin) add("Admin Panel")
    if (isPremium) addAll(listOf("Analytics", "Export"))
}

val config = buildMap {
    put("version", "1.0")
    put("debug", isDebugBuild)
    if (useCache) put("cache_ttl", 3600)
}
Enter fullscreen mode Exit fullscreen mode

Custom DSL with Receiver Lambdas

Create a UI builder DSL:

class HtmlBuilder {
    private val children = mutableListOf<String>()

    fun p(text: String) { children.add("<p>$text</p>") }
    fun h1(text: String) { children.add("<h1>$text</h1>") }
    fun button(text: String, action: String) {
        children.add("<button onclick='$action'>$text</button>")
    }

    fun build() = children.joinToString("")
}

fun html(init: HtmlBuilder.() -> Unit): String {
    return HtmlBuilder().apply(init).build()
}

val page = html {
    h1("Welcome")
    p("Click the button below")
    button("Click Me", "handleClick()")
}
Enter fullscreen mode Exit fullscreen mode

@DslMarker for Scope Control

Prevent scope nesting mistakes:

@DslMarker
annotation class HtmlDsl

@HtmlDsl
class HtmlBuilder {
    fun h1(text: String) { }
    fun p(init: HtmlBuilder.() -> Unit) { } // nested builder
}
Enter fullscreen mode Exit fullscreen mode

Nested DSL Patterns

fun container(init: HtmlBuilder.() -> Unit) = HtmlBuilder().apply(init)

val layout = container {
    h1("Title")
    p {
        // This works: inner builder context
    }
}
Enter fullscreen mode Exit fullscreen mode

DSLs make configuration expressive and type-safe.


8 production-ready Android app templates available on Gumroad.
Browse templatesGumroad

Top comments (0)