Type-safe builders
- Kotlin course
- Expert
- Lesson 5/47
- 5 min
Learn type-safe builders with @DslMarker.
What does @DslMarker do?
When writing a DSL, nested lambda blocks may accidentally use the receiver of an outer block. @DslMarker narrows that scope.
With @DslMarker annotation class MenuDsl you declare a marker. All receivers carrying the same marker cannot mix in nested blocks. So inside item { name = "Soup" } you cannot reach the outer menu block by mistake. This is a compile-time guard; no runtime cost. It appears in DSLs like html { ... } in the Kotlin standard library.
A builder DSL
To write a type-safe builder, you add the @MenuDsl marker to a class and give it an extension function.
Example: @MenuDsl class Menu { val items = mutableListOf<Item>(); fun item(block: Item.() -> Unit) { items.add(Item().apply(block)) } }. The outer entry is fun menu(block: Menu.() -> Unit): Menu = Menu().apply(block). This lets you write menu { item { name = "Soup" } }. Thanks to @DslMarker, the item block cannot accidentally touch the items list of the outer menu.
Practice Write the menu DSL
Define @DslMarker annotation class MenuDsl. Class Item has var name: String = "" and var price: Int = 0. Inside @MenuDsl class Menu add val items = mutableListOf<Item>() and fun item(block: Item.() -> Unit). Write the menu function and in main add two items, then write println(m.items.size).
Starter code
// Write @DslMarker annotation and Menu DSL
class Item {
var name: String = ""
var price: Int = 0
}
// Add the Menu class and menu function
fun main() {
val m = menu {
item { name = "Soup"; price = 30 }
item { name = "Rice"; price = 45 }
}
// print the item count
}Sample solution
@DslMarker
annotation class MenuDsl
class Item { var name: String = ""; var price: Int = 0 }
@MenuDsl
class Menu {
val items = mutableListOf<Item>()
fun item(block: Item.() -> Unit) { items.add(Item().apply(block)) }
}
fun menu(block: Menu.() -> Unit): Menu = Menu().apply(block)
fun main() {
val m = menu {
item { name = "Soup"; price = 30 }
item { name = "Rice"; price = 45 }
}
println(m.items.size)
}Expected output
2
Questions and answers
Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.
1 Define the @DslMarker annotation
____
annotation class MenuDsl
@DslMarker
annotation class MenuDsl
Explanation: @DslMarker creates a DSL marker class.
2 What does @DslMarker prevent?
- A runtime error in a lambda
- Compiling inline functions
- Defining an annotation class
- Wrong use of an outer receiver in nested DSL blocks Correct answer
Explanation: @DslMarker stops accidental access to the outer receiver from inner blocks.
3 Match builder parts
| Term | Meaning |
|---|---|
@DslMarker |
Marker definition |
annotation class |
Marker class |
@MenuDsl |
Receiver marker |
block: Item.() -> Unit |
Lambda receiver type |