Table of Contents
Kotlin での DSL の書き方についてまとめました。
Kotlin は 内部の分離されたDSL作成を支援してくれます。 DSL作成では特に Lambda を活用することになります。 DSL の分類について知りたい方は DSL, DOMAIN SPECIFIC LANGUAGE の分類 をご参照ください。
DSLを作成する際の書き方はいくつかありますが、 Kotlin のドキュメントの TypeSafeBuilders に倣ってクラスを作成します。 他に、 Kotlin DSL from Theory to Practice のように Builderクラスを作って組み合わせるやり方もあります。
サンプルコード
DSL を記述するためのクラス・関数を用意します。
このように書くと、次のように クラスA のオブジェクトを作ることができます。
上記サンプルコードの問題点
上のサンプルコードに DSL の基本が詰まっています。 しかしこのままでは、次のようなコードも記述可能です。 ルール上可能ですが、非常に見通しが悪くなります。
具体的な問題項目は次の通りです。
- B, C を定義するブロックの中で A の属性が代入されている。
- B を定義するブロックの中で、さらに B を定義する関数とそのブロックが記述されている。
@DslMarker
Kotlin 1.1 から導入されているアノテーション @DslMarker
を用いて上の問題を解決できます。 @DslMarker
はDSLを作成する際の、スコープを制御するために使えるアノテーションです。 このアノテーションは、アノテーションクラスに付与します。
@DslMarker
を付けたアノテーションクラス(DslMarkerTest
)を作成し、 DSLを生成するクラスに付与します。 クラス A, B にアノテーションDslMarkerTest
を付与します。
このようにすると、次のようなコードはエラーとなります。
@DslMarker
を付与したアノテーション @DslMarkerTest
を付与した2つ以上のクラスのオブジェクトが暗黙的レシーバ、すなわち this
になりうる場合、それらのオブジェクトのうち一番最後に this
になるオブジェクト以外は this
として使えません。
上のサンプルコードでは、 メソッドaに渡すラムダの中では @DslMarkerTest
のついたクラスAのインスタンスが this
となっていますが、 this.b
に渡すラムダの中では新しく @DslMarkerTest
のついたクラスBのインスタンスが this
となるため、 その前に this
となっていた クラスAのインスタンスは this
としては使えなくなります。