Kotlin 类型的安全构建器 DSL,最外层功能的安全性
Kotlin type safe builder DSLs, safety for the outermost function
我将使用实现 DSL 的文档中的 official example 进行一些 HTML 创建。
从 Kotlin 1.1 开始,@DslMarker
注释允许我们限制 类 中函数的范围,就像示例中使用 @HtmlTagMarker
注释一样。当我们尝试编写结构不正确的代码时,这会给我们一个错误:
html {
body {
body { // this in an error, as it's a function call on the outside Html element
}
}
}
但是,这不会阻止嵌套最外层的函数,它是 DSL 的入口点。比如现在这个例子,这样写是没有问题的:
html {
html {
}
}
在这方面有没有办法让 DSL 更安全?
可能这可以以更优雅的方式完成,但我建议在具有为接收器类型定义的匹配签名的函数上使用 @Deprecated
注释和 DeprecationLevel.ERROR
,例如:
@Deprecated("Cannot be used in a html block.", level = DeprecationLevel.ERROR)
fun HtmlReceiver.html(action: HtmlReceiver.() -> Unit): Nothing = error("...")
或者这可以是一个成员函数。顺便说一下,IDE 完成的行为根据它是扩展还是成员而有所不同。
这将使内部调用无效:
html {
html { // Error: Cannot be used in a html block.
}
}
顶级函数仍然可以通过其 FQN 在 DSL 块内调用,例如com.example.html { }
,所以这个技巧只是防止用户误调用顶层函数。
我将使用实现 DSL 的文档中的 official example 进行一些 HTML 创建。
从 Kotlin 1.1 开始,@DslMarker
注释允许我们限制 类 中函数的范围,就像示例中使用 @HtmlTagMarker
注释一样。当我们尝试编写结构不正确的代码时,这会给我们一个错误:
html {
body {
body { // this in an error, as it's a function call on the outside Html element
}
}
}
但是,这不会阻止嵌套最外层的函数,它是 DSL 的入口点。比如现在这个例子,这样写是没有问题的:
html {
html {
}
}
在这方面有没有办法让 DSL 更安全?
可能这可以以更优雅的方式完成,但我建议在具有为接收器类型定义的匹配签名的函数上使用 @Deprecated
注释和 DeprecationLevel.ERROR
,例如:
@Deprecated("Cannot be used in a html block.", level = DeprecationLevel.ERROR)
fun HtmlReceiver.html(action: HtmlReceiver.() -> Unit): Nothing = error("...")
或者这可以是一个成员函数。顺便说一下,IDE 完成的行为根据它是扩展还是成员而有所不同。
这将使内部调用无效:
html {
html { // Error: Cannot be used in a html block.
}
}
顶级函数仍然可以通过其 FQN 在 DSL 块内调用,例如com.example.html { }
,所以这个技巧只是防止用户误调用顶层函数。