Type-safe builders help you create complex objects step-by-step with clear rules. They make your code easier to read and safer by catching mistakes early.
Building blocks of type-safe builders in Kotlin
class Builder { fun part(block: PartBuilder.() -> Unit) { /*...*/ } fun build(): Result { /*...*/ } } class PartBuilder { fun setProperty(value: Any) { /*...*/ } } fun buildObject(block: Builder.() -> Unit): Result { val builder = Builder() builder.block() return builder.build() }
The builder class defines how to add parts and build the final object.
The block parameter uses a lambda with receiver to allow clean syntax inside the builder.
class MealBuilder { private val items = mutableListOf<String>() fun addItem(item: String) { items.add(item) } fun build() = items.joinToString(", ") } fun meal(block: MealBuilder.() -> Unit): String { val builder = MealBuilder() builder.block() return builder.build() }
val myMeal = meal { addItem("Burger") addItem("Fries") addItem("Soda") } println(myMeal)
This program builds a simple HTML page using type-safe builders for <html>, <head>, and <body> sections. It shows how to organize code clearly and safely.
class HtmlBuilder { private val children = mutableListOf<String>() fun head(block: HeadBuilder.() -> Unit) { val headBuilder = HeadBuilder() headBuilder.block() children.add("<head>${headBuilder.build()}</head>") } fun body(block: BodyBuilder.() -> Unit) { val bodyBuilder = BodyBuilder() bodyBuilder.block() children.add("<body>${bodyBuilder.build()}</body>") } fun build() = "<html>" + children.joinToString("") + "</html>" } class HeadBuilder { private val elements = mutableListOf<String>() fun title(text: String) { elements.add("<title>$text</title>") } fun build() = elements.joinToString("") } class BodyBuilder { private val elements = mutableListOf<String>() fun p(text: String) { elements.add("<p>$text</p>") } fun build() = elements.joinToString("") } fun html(block: HtmlBuilder.() -> Unit): String { val builder = HtmlBuilder() builder.block() return builder.build() } fun main() { val page = html { head { title("Welcome") } body { p("Hello, this is a type-safe builder example.") } } println(page) }
Type-safe builders use lambdas with receivers to create a clean and readable syntax.
They help prevent errors by limiting what can be called inside each builder block.
You can nest builders to represent complex structures like HTML or UI layouts.
Type-safe builders help build complex objects step-by-step with clear rules.
They use special functions and lambdas to make code easy to read and safe.
You can create nested builders to represent structured data like HTML.
Practice
Solution
Step 1: Understand the concept of type-safe builders
Type-safe builders allow building complex objects in a structured and safe way using Kotlin's DSL features.Step 2: Identify the main purpose
The main goal is to create objects step-by-step with clear rules, avoiding errors and improving readability.Final Answer:
To create complex objects step-by-step with safety and clarity -> Option AQuick Check:
Type-safe builders = safe, clear object creation [OK]
- Confusing builders with loops or exception handling
- Thinking they convert data types automatically
html that takes a lambda with receiver?Solution
Step 1: Identify lambda with receiver syntax
Type-safe builders use lambdas with receiver, which have the formReceiverType.() -> ReturnType.Step 2: Match the correct function signature
The function should accept a lambda with receiver Html and return Unit, soHtml.() -> Unitis correct.Final Answer:
fun html(block: Html.() -> Unit) { /*...*/ } -> Option CQuick Check:
Lambda with receiver = Html.() -> Unit [OK]
- Using regular lambda (Html) -> Unit instead of lambda with receiver
- Omitting the receiver type in the lambda
- Returning Html instead of Unit
class Html {
val children = mutableListOf<String>()
fun body(block: Body.() -> Unit) {
val body = Body()
body.block()
children.addAll(body.contents)
}
override fun toString() = children.joinToString(", ")
}
class Body {
val contents = mutableListOf<String>()
fun p(text: String) {
contents.add("$text
")
}
}
fun html(block: Html.() -> Unit): Html {
val html = Html()
html.block()
return html
}
fun main() {
val result = html {
body {
p("Hello")
p("World")
}
}
println(result)
}Solution
Step 1: Understand the builder structure
Thehtmlfunction creates an Html object, callsbodywhich creates a Body object, then callsptwice adding <p>Hello</p> and <p>World</p> to Body's contents.Step 2: Check how children are collected and printed
The Html object's children list gets Body's contents added, thentoString()joins them with ", ". So output is "<p>Hello</p>, <p>World</p>".Final Answer:
<p>Hello</p>, <p>World</p> -> Option BQuick Check:
Children list joined = <p>Hello</p>, <p>World</p> [OK]
- Assuming HTML tags are printed as is without escaping
- Expecting full HTML tags like <body> in output
- Thinking code causes compilation error
class Html {
fun body(block: Body.() -> Unit) {
val body = Body()
block()
}
}
class Body {
fun p(text: String) { /*...*/ }
}
fun main() {
val html = Html()
html.body {
p("Hello")
}
}Solution
Step 1: Check how the lambda is called inside body()
The lambdablockis called asblock()without receiver, so inside the lambda 'p' is not recognized.Step 2: Identify correct lambda invocation
It should be called asbody.block()to set the receiver to the Body instance, allowing 'p' to be resolved.Final Answer:
The lambda is called without receiver, so 'p' is unresolved -> Option AQuick Check:
Lambda call without receiver = unresolved 'p' [OK]
- Calling lambda without receiver inside builder
- Assuming inheritance fixes builder scope
- Expecting return value from builder function
div inside body, while keeping type safety?Solution
Step 1: Understand nested builder pattern
To support nested elements, define functions inside builder classes that accept lambdas with receivers of nested element classes.Step 2: Apply to 'div' inside 'body'
InsideBody, define adivfunction takingDiv.() -> Unitlambda, creating a Div instance and applying the lambda to it.Final Answer:
Define adivfunction insideBodythat takes a lambda with receiver of a newDivclass -> Option DQuick Check:
Nested builders = functions with lambdas with receiver [OK]
- Using simple properties instead of nested builder functions
- Trying inheritance instead of composition
- Defining builder functions outside the class scope
Ready to prove it?
Pass the test and this pattern moves unknown
