Bird
Raised Fist0
Kotlinprogramming~10 mins

Building blocks of type-safe builders in Kotlin - Step-by-Step Execution

Choose your learning style10 modes available
Concept Flow - Building blocks of type-safe builders
Start Builder Function
↓
Create Receiver Object
↓
Apply Lambda with Receiver
↓
Inside Lambda: Configure Object
↓
Return Configured Object
↓
End
The builder function creates an object, applies a lambda to configure it safely, then returns the configured object.
Execution Sample
Kotlin
class Person(var name: String = "", var age: Int = 0)

fun person(block: Person.() -> Unit): Person {
    val p = Person()
    p.block()
    return p
}

val p = person {
    name = "Alice"
    age = 30
}
This code builds a Person object using a type-safe builder lambda to set properties.
Execution Table
StepActionEvaluationResult
1Call person builder functionperson { ... }Enter builder function
2Create Person instanceval p = Person()p = Person(name="", age=0)
3Apply lambda block on pp.block()Inside lambda: set name and age
4Set name propertyname = "Alice"p.name = "Alice"
5Set age propertyage = 30p.age = 30
6Return configured Personreturn pPerson(name="Alice", age=30)
7Assign to variable pval p = ...p = Person(name="Alice", age=30)
💡 Builder function ends after returning the configured Person object.
Variable Tracker
VariableStartAfter Step 2After Step 4After Step 5Final
pundefinedPerson(name="", age=0)Person(name="Alice", age=0)Person(name="Alice", age=30)Person(name="Alice", age=30)
Key Moments - 3 Insights
Why can we call 'name = "Alice"' inside the lambda without specifying the object?
Inside the lambda with receiver, 'this' refers to the Person instance 'p', so properties can be accessed directly as shown in step 4.
What ensures that only valid Person properties can be set inside the lambda?
The lambda has a receiver type Person, so only Person's members are accessible, preventing invalid property assignments (see step 3).
Why do we return 'p' at the end of the builder function?
Returning 'p' gives the caller the fully configured Person object after the lambda has applied all changes (step 6).
Visual Quiz - 3 Questions
Test your understanding
Look at the execution table, what is the value of 'p.name' after step 4?
Anull
B"" (empty string)
C"Alice"
D30
💡 Hint
Check the 'Result' column at step 4 in the execution table.
At which step does the lambda start configuring the Person object?
AStep 3
BStep 2
CStep 5
DStep 7
💡 Hint
Look for 'Apply lambda block on p' in the execution table.
If we remove 'return p' from the builder function, what happens?
AThe function returns the configured Person object anyway.
BThe function returns Unit (no object), so 'p' outside is not assigned.
CThe lambda won't run.
DThe Person object is created twice.
💡 Hint
Refer to step 6 where 'return p' is crucial for returning the object.
Concept Snapshot
Type-safe builders use lambdas with receivers to configure objects.
Syntax: fun builder(block: Receiver.() -> Unit): Receiver
Inside builder: create object, apply block, return object.
Allows direct access to object properties inside lambda.
Ensures only valid properties can be set safely.
Full Transcript
This example shows how a type-safe builder works in Kotlin. The builder function creates a Person object, then applies a lambda with receiver to configure its properties. Inside the lambda, we can set 'name' and 'age' directly because the lambda's receiver is the Person instance. After configuration, the builder returns the fully set up Person object. This pattern helps write clear and safe code for building objects step-by-step.

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

  1. 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.
  2. Step 2: Identify the main purpose

    The main goal is to create objects step-by-step with clear rules, avoiding errors and improving readability.
  3. Final Answer:

    To create complex objects step-by-step with safety and clarity -> Option A
  4. Quick Check:

    Type-safe builders = safe, clear object creation [OK]
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

  1. Step 1: Identify lambda with receiver syntax

    Type-safe builders use lambdas with receiver, which have the form ReceiverType.() -> ReturnType.
  2. Step 2: Match the correct function signature

    The function should accept a lambda with receiver Html and return Unit, so Html.() -> Unit is correct.
  3. Final Answer:

    fun html(block: Html.() -> Unit) { /*...*/ } -> Option C
  4. 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

  1. 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.
  2. 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>".
  3. Final Answer:

    <p>Hello</p>, <p>World</p> -> Option B
  4. 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

  1. 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.
  2. 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.
  3. Final Answer:

    The lambda is called without receiver, so 'p' is unresolved -> Option A
  4. 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

  1. Step 1: Understand nested builder pattern

    To support nested elements, define functions inside builder classes that accept lambdas with receivers of nested element classes.
  2. 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.
  3. Final Answer:

    Define a div function inside Body that takes a lambda with receiver of a new Div class -> Option D
  4. 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 unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard