0Pricing
Kotlin Academy · Урок

@DslMarker: предотвращение утечки получателя

Используйте аннотации @DslMarker, чтобы проверять правила вложенности DSL во время компиляции.

«@DslMarker: предотвращение утечки получателя» — бесплатный урок Kotlin Academy на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Kotlin Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Kotlin Academy содержит 4 уроков всего.

Проблема утечки получателя

Во вложенных DSL внутренняя лямбда функции-построителя может случайно получить доступ к членам внешнего получателя. Например, внутри блока td { } можно вызвать tr { } из внешней области видимости таблицы — что почти никогда не предполагается.

Как выглядит утечка

Без @DslMarker компилятор разрешает вызывать функции из внешней области видимости внутри любой лямбды, что приводит к запутанному и подверженному ошибкам использованию DSL:

// Without @DslMarker — this compiles but is wrong:
html {
    body {
        table {
            tr {                // outer scope leaks in
                tr {  }         // calling tr inside tr — nonsensical
            }
        }
    }
}

Введение @DslMarker

@DslMarker — это метааннотация, которую применяют к собственному классу аннотации. У любого класса-построителя, помеченного вашей аннотацией DSL, неявный получатель будет скрыт во вложенных лямбдах, использующих ту же аннотацию DSL.

@DslMarker
annotation class HtmlDsl

@HtmlDsl
class Table

@HtmlDsl
class Tr

Как работает скрытие неявного получателя

Когда две вложенные лямбды имеют получателей, помеченных одной и той же аннотацией @DslMarker, внешний получатель скрывается внутри внутренней лямбды. Прямой доступ к нему вызывает ошибку компиляции.

@DslMarker
annotation class HtmlDsl

@HtmlDsl class Html
@HtmlDsl class Body
@HtmlDsl class Div

Полный пример с @DslMarker

Объявите аннотацию, пометьте классы-построители, и тогда вложенные вызовы методов из внешней области видимости будут обнаруживаться во время компиляции:

@DslMarker
annotation class MyCfgDsl

@MyCfgDsl
class ServerBuilder {
    var port: Int = 8080
    fun database(block: DbBuilder.() -> Unit) { /*...*/ }
}

@MyCfgDsl
class DbBuilder {
    var url: String = ""
    // Cannot call ServerBuilder.port here — compiler error
}

Обход ограничения с помощью this@

Если вам действительно нужно обратиться к внешнему получателю из внутренней лямбды, используйте ссылку с меткой this@OuterClass. Такой доступ явный и намеренный, в отличие от случайной утечки.

fun ServerBuilder.database(block: DbBuilder.() -> Unit) {
    val db = DbBuilder()
    db.block()
    val serverPort = this@ServerBuilder.port  // explicit outer access
}

@DslMarker с несколькими DSL

Разные DSL должны использовать разные аннотации @DslMarker. Два не связанных между собой DSL с разными маркерами не мешают друг другу даже при вложении.

@DslMarker annotation class HtmlDsl
@DslMarker annotation class GradleDsl
// HtmlDsl and GradleDsl receivers do not hide each other

Применение к функциям-построителям и получателям

Примените аннотацию DSL к самому классу-построителю. Аннотация распространяется на любой параметр лямбды, тип получателя которого — этот класс. Дополнительная аннотация отдельных функций не требуется.

Поддержка IDE

IntelliJ IDEA и Android Studio сразу выделяют доступ к внешнему получателю как ошибку, если используется @DslMarker. Это обеспечивает обратную связь в реальном времени при создании или использовании DSL.

Проектирование с учётом безопасности

Хорошее проектирование DSL связывает каждый класс-построитель с @DslMarker. Небольшие накладные расходы на аннотацию значительно повышают удобство DSL и предотвращают целые категории ошибок пользователей DSL.

Использование в стандартной библиотеке

Собственная аннотация @DslMarker Kotlin используется в kotlinx.html, DSL маршрутизации Ktor, скрипте Kotlin для Gradle и функциях-построителях buildMap/buildList. Это фундаментальный инструмент любого серьёзного набора средств для создания DSL.

Быстрая проверка

Что предотвращает @DslMarker?

Итоги: @DslMarker

Основные выводы:

  • Без @DslMarker внешние получатели проникают во внутренние лямбды DSL
  • Определите класс аннотации с метааннотацией и примените его ко всем классам-построителям в вашем DSL
  • Вложенные лямбды с одним и тем же маркером не могут случайно вызывать члены внешней области видимости
  • Используйте this@OuterClass, когда действительно нужен доступ к внешней области видимости

Часто задаваемые вопросы

Урок «@DslMarker: предотвращение утечки получателя» бесплатный?

Да — полный текст урока «@DslMarker: предотвращение утечки получателя» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Kotlin Academy, подпишись на CoddyKit PRO. Курс Kotlin Academy содержит 4 уроков всего.

Чему я научусь в уроке «@DslMarker: предотвращение утечки получателя»?

Используйте аннотации @DslMarker, чтобы проверять правила вложенности DSL во время компиляции. Ты практикуешь Kotlin Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Kotlin Academy?

Предыдущий опыт не требуется. Kotlin Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.

Сколько времени занимает урок «@DslMarker: предотвращение утечки получателя»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Kotlin Academy?

Да. Каждый урок Kotlin Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Лямбда с получателем: основа DSL
  2. @DslMarker: предотвращение утечки получателя
  3. Создание типобезопасного DSL для HTML/конфигурации
  4. Тестирование и развитие DSL без нарушения совместимости
← Назад к Kotlin Academy