📖 Scenario: Imagine you want to create a simple HTML-like structure in Kotlin using type-safe builders. This helps you write code that looks like HTML but is checked by the compiler to avoid mistakes.
🎯 Goal: You will build a small type-safe builder that creates a simple HTML structure with a html root, a body, and some p paragraphs inside. You will learn how to create builder classes and use lambda functions to build nested structures safely.
📋 What You'll Learn
Create a class called HTML with a body function
Create a class called Body with a p function
Use lambda functions with receiver to build nested elements
Create a html function that starts the building process
Print the built HTML structure as a string
💡 Why This Matters
🌍 Real World
Type-safe builders are used in Kotlin to create DSLs (domain-specific languages) that make code easier to read and write, such as building HTML, XML, or UI layouts.
💼 Career
Understanding type-safe builders helps you work with Kotlin DSLs in real projects, improving code safety and clarity, which is valuable for Android development and backend services.
Progress0 / 4 steps
1
Create the HTML and Body classes
Create a class called HTML with a mutable list of strings called content. Also create a class called Body with a mutable list of strings called paragraphs.
Kotlin
Hint
Use class keyword to create classes and mutableListOf<String>() to create lists.
2
Add builder functions to HTML and Body
Add a function body inside HTML that takes a lambda with receiver Body.() -> Unit. Inside it, create a Body instance, call the lambda on it, and add all paragraphs from Body to content. Also add a function p inside Body that takes a String and adds it to paragraphs.
Kotlin
Hint
Use lambda with receiver to build nested structures. Add paragraphs as strings with <p> tags.
3
Create the html builder function
Create a function called html that takes a lambda with receiver HTML.() -> Unit. Inside it, create an HTML instance, call the lambda on it, and return the HTML instance.
Kotlin
Hint
This function starts the building process by creating an HTML object and applying the lambda.
4
Build and print the HTML structure
Use the html function to build an HTML structure with a body containing two paragraphs: "Hello, world!" and "Welcome to type-safe builders.". Then print the content of the HTML instance as a single string by joining the content list.
Kotlin
Hint
Use the html function with nested body and p calls. Then print the joined content.
Practice
(1/5)
1. What is the main purpose of type-safe builders in Kotlin?
easy
A. To create complex objects step-by-step with safety and clarity
B. To write faster loops for collections
C. To handle exceptions automatically
D. To convert data types implicitly
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 A
Hint: Type-safe builders help build objects safely step-by-step [OK]
Common Mistakes:
Confusing builders with loops or exception handling
Thinking they convert data types automatically
2. Which Kotlin syntax correctly defines a type-safe builder function named html that takes a lambda with receiver?
easy
A. fun html(block: (Html) -> Unit) { /*...*/ }
B. fun html(block: () -> Html) { /*...*/ }
C. fun html(block: Html.() -> Unit) { /*...*/ }
D. fun html(block: () -> Unit) { /*...*/ }
Solution
Step 1: Identify lambda with receiver syntax
Type-safe builders use lambdas with receiver, which have the form ReceiverType.() -> ReturnType.
Step 2: Match the correct function signature
The function should accept a lambda with receiver Html and return Unit, so Html.() -> Unit is correct.
Final Answer:
fun html(block: Html.() -> Unit) { /*...*/ } -> Option C
Quick Check:
Lambda with receiver = Html.() -> Unit [OK]
Hint: Look for lambda with receiver syntax ReceiverType.() -> Unit [OK]
Common Mistakes:
Using regular lambda (Html) -> Unit instead of lambda with receiver
Omitting the receiver type in the lambda
Returning Html instead of Unit
3. Given the following Kotlin code, what is the output?
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)
}
medium
A. Hello, World
B. <p>Hello</p>, <p>World</p>
C. <body><p>Hello</p><p>World</p></body>
D. Compilation error
Solution
Step 1: Understand the builder structure
The html function creates an Html object, calls body which creates a Body object, then calls p twice 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, then toString() joins them with ", ". So output is "<p>Hello</p>, <p>World</p>".
Final Answer:
<p>Hello</p>, <p>World</p> -> Option B
Quick Check:
Children list joined = <p>Hello</p>, <p>World</p> [OK]
Hint: Look for toString join of children list for output [OK]
Common Mistakes:
Assuming HTML tags are printed as is without escaping
Expecting full HTML tags like <body> in output
Thinking code causes compilation error
4. What is the error in this Kotlin builder code snippet?
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")
}
}
medium
A. The lambda is called without receiver, so 'p' is unresolved
B. Body class is missing a constructor
C. Html class should inherit from Body
D. The 'body' function should return a value
Solution
Step 1: Check how the lambda is called inside body()
The lambda block is called as block() without receiver, so inside the lambda 'p' is not recognized.
Step 2: Identify correct lambda invocation
It should be called as body.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 A
Quick Check:
Lambda call without receiver = unresolved 'p' [OK]
Hint: Call lambda with receiver: use receiver.lambda() syntax [OK]
Common Mistakes:
Calling lambda without receiver inside builder
Assuming inheritance fixes builder scope
Expecting return value from builder function
5. How can you extend a type-safe builder to support nested elements, for example adding a div inside body, while keeping type safety?
hard
A. Use a global div function outside the builder classes
B. Add a div property of type String to Body
C. Make Body inherit from Div
D. Define a div function inside Body that takes a lambda with receiver of a new Div class
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'
Inside Body, define a div function taking Div.() -> Unit lambda, creating a Div instance and applying the lambda to it.
Final Answer:
Define a div function inside Body that takes a lambda with receiver of a new Div class -> Option D
Quick Check:
Nested builders = functions with lambdas with receiver [OK]
Hint: Nested builders use functions with lambdas with receiver inside classes [OK]
Common Mistakes:
Using simple properties instead of nested builder functions
Trying inheritance instead of composition
Defining builder functions outside the class scope
Recommended
Ready to prove it?
Pass the test and this pattern moves unknownstrongwhile earning +1 Grip toward your certificate.