Метка области в Kotlin
Во вложенном построителе внутренний блок иногда случайно вызывает метод внешнего получателя. Такой вызов компилируется, но ломает читаемость и путает уровни DSL.
Аннотация @DslMarker помечает семейство
типов-построителей. Ее вешают на свою аннотацию,
а ту - на классы получателей. Компилятор запрещает
неявный доступ к внешнему this из
вложенного блока с той же меткой:
@DslMarker
annotation class UiMarker
@UiMarker
class Screen {
val items = mutableListOf<String>()
fun header(text: String) {
items.add("h:$text")
}
fun group(block: Group.() -> Unit) {
Group(this).block()
}
@UiMarker
class Group(private val screen: Screen) {
fun line(text: String) {
screen.items.add("p:$text")
}
}
}
Если внутри group попытаться вызвать
header внешнего экрана, компилятор
сообщит об ошибке: вложенный получатель не
видит методы родительского уровня с общей
меткой. Заголовок нужно задавать во внешнем
блоке, а строки - во внутреннем:
fun buildScreen(block: Screen.() -> Unit): List<String> {
val screen = Screen()
screen.block()
return screen.items
}
fun main() {
val menu = buildScreen {
header("home")
group {
line("news")
line("blog")
}
}
println(menu)
}
Явный доступ через имя внешнего объекта метка не отменяет, но в DSL его обычно избегают. Разделение уровней делает структуру блоков предсказуемой для читателя.
Пометьте аннотацию @DslMarker и повесьте
ее на внешний Deck и внутренний
Slide. Заголовок колоды задайте
снаружи, пункты - внутри slide.
Соберите две группы с разным числом строк и
выведите итоговый список меток. Проверьте, что
внутренний блок не вызывает методы Deck
напрямую.