Kotlin Academy · レッスン

@DslMarker:レシーバーの漏洩を防ぐ

@DslMarkerアノテーションを使い、コンパイル時にDSLのネスト規則を適用します。

レッスン 2/413 ステップ

「@DslMarker:レシーバーの漏洩を防ぐ」はCoddyKit上の無料Kotlin Academyレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これは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

暗黙のレシーバーを非表示にする仕組み

入れ子になった 2 つのラムダが、同じ @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
}

複数の DSL での @DslMarker

異なる DSL では、異なる @DslMarker アノテーションを使用してください。互いに関係のない 2 つの DSL は、入れ子になっていても異なるマーカーを使っていれば干渉しません。

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

ビルダーとレシーバーに適用する

DSL のアノテーションはビルダークラス自体に適用してください。このアノテーションは、そのクラスをレシーバー型とするラムダパラメーターに伝播します。個々の関数にさらにアノテーションを付ける必要はありません。

IDE のサポート

@DslMarker を使用すると、IntelliJ IDEA と Android Studio は外側のレシーバーへのアクセスを即座にエラーとして強調表示します。これにより、DSL を作成するときも使用するときも、リアルタイムのフィードバックを得られます。

安全性を考慮した設計

優れた DSL 設計では、各ビルダークラスに @DslMarker を組み合わせます。アノテーションの追加はわずかな負担ですが、DSL の使いやすさを大幅に向上させ、DSL 利用者に起こりうるさまざまなバグを防ぎます。

標準ライブラリでの使用例

Kotlin 自身の @DslMarker は、kotlinx.html、Ktor のルーティング DSL、Gradle Kotlin スクリプト、buildMap/buildList ビルダーで使用されています。これは、本格的な DSL ツールキットに欠かせない基本的なツールです。

クイックチェック

@DslMarker は何を防ぎますか。

まとめ: @DslMarker

重要なポイント:

  • @DslMarker がないと、外側のレシーバーが内側の DSL ラムダに漏れ込みます
  • メタアノテーションが付いたアノテーションクラスを定義し、DSL 内のすべてのビルダークラスに適用します
  • 同じマーカーを持つ入れ子のラムダからは、外側のスコープのメンバーを誤って呼び出せません
  • 外側のスコープに本当にアクセスする必要がある場合は、this@OuterClass を使用します
無料で開始

AI チューターと学ぶ Kotlin — 無料

ブラウザでリアルコードを書いて実行し、24/7 の AI チューターから瞬時にサポートを受け、ウェブまたはアプリで続きから学習できます。

コース
51
レッスン
203

よくある質問

「@DslMarker:レシーバーの漏洩を防ぐ」レッスンは無料ですか?

はい。「@DslMarker:レシーバーの漏洩を防ぐ」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Kotlin Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Kotlin Academyコースには全4レッスンが含まれています。

「@DslMarker:レシーバーの漏洩を防ぐ」で何を学びますか?

@DslMarkerアノテーションを使い、コンパイル時にDSLのネスト規則を適用します。 ブラウザで直接実行するハンズオンコードでKotlin Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Kotlin Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのKotlin Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。

「@DslMarker:レシーバーの漏洩を防ぐ」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このKotlin Academyレッスンでコードを書いて実行できますか?

はい。すべてのKotlin Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. レシーバー付きLambda:DSLの基盤
  2. @DslMarker:レシーバーの漏洩を防ぐ
  3. 型安全なHTML/Config DSLの構築
  4. ユーザーを壊さずにDSLをテスト・進化させる
← Kotlin Academyに戻る