Kotlin型安全制造商DSLs,用于最外层功能的安全

我将使用实现DSL进行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更安全?

也许这可以以一种更优雅的方式完成,但是我可以建议在带有为接收器类型定义的匹配签名的函数上使用带有DeprecationLevel.ERROR@Deprecated注释,例如:

 @Deprecated("Cannot be used in a html block.", level = DeprecationLevel.ERROR) fun HtmlReceiver.html(action: HtmlReceiver.() -> Unit): Nothing = error("...") 

或者这可以是一个成员函数。 顺便说一下,根据IDE扩展或成员是否完成,IDE完成行为有点不同。

这会使内部的呼叫无效:

 html { html { // Error: Cannot be used in a html block. } } 

(这个代码的演示)

顶级函数仍然可以通过其FQN例如com.example.html { }在一个DSL块内被调用,所以这个技巧只能保护用户不会错误地调用顶级函数。