DSL の書き方
Kotlin で DSL を書く場合のサンプルコードです。
class A {
var valueA: String? = null
var b: B? = null
fun b(init: B.() -> Unit): B {
val b = B()
b.init()
this.b = b
return b
}
}
class B {
var valueB: String? = null
var c: C? = null
fun c(init: C.() -> Unit): C {
val c = C()
c.init()
this.c = c
return c
}
}
class C {
var valueC: String? = null
}
fun a(init: A.() -> Unit): A {
val a = A()
a.init()
return a
}
このように書くと、次のように クラスA のオブジェクトを作ることができます。
a {
valueA = "A"
b {
valueB = "B"
c {
valueC = "C"
}
}
}
@DslMarker
上のサンプルでは、次のようなコードも書けてしまいます。
a {
valueA = "A"
b {
valueB = "B"
c {
valueA = "A"
valueC = "C"
}
b {
valueA = "A"
valueB = "B"
b {
valueB = "B"
}
}
}
}
具体的な問題項目は次の通りです。
- B, C を定義するブロックの中で A の属性が代入されている。
- B を定義するブロックの中で、さらに B を定義する関数とそのブロックが記述されている。
- B は B をプロパティにもたないのに
これを Kotlin 1.1 から導入されているアノテーション @DslMarker
が解決します。 @DslMarker
はDSLを作成する際の、スコープを制御するために使えるアノテーションです。 このアノテーションは、アノテーションクラスに付与します。
@DslMarker
を付けたアノテーションクラス(DslMarkerTest
)を作成し、 DSLを生成するクラスに付与します。 クラス A, B にアノテーションDslMarkerTest
を付与します。
@DslMarker
annotation class DslMarkerTest
@DslMarkerTest
class A { ... }
@DslMarkerTest
class B { ... }
class C { ... }
こうすると、上のコードがエラーになります。
(この記事は拙ブログから抜粋・編集したものです。)