错误的“this”被用在嵌套闭包中
我试图保持这个最小,但让我知道,如果我太小了。
假设你有这样的类层次结构,为生成HTML而设计(受到Kotlin教程的启发;下面是半伪代码):
class Tag { protected val children = arrayListOf<Tag>() operator fun String.unaryPlus() = children.add(Text(this)) } class TagWithChildren : Tag() { fun head(init: Head.() -> Unit) = initializeTag(Head(), init) fun script(init: Script.() -> Unit) = initializeTag(Script(), init) fun <T : Tag> initializeTag(tag: T, init: T.() -> Unit): T { tag.init() children.add(tag) return tag } } class Head : TagWithChildren() class Script : Tag() class Text(val str: Text) : Tag()
注意Head
有head
和script
方法,而Script
没有。
现在你可以构建一个如下所示的模板:
head { script { +"alert('hi');" } }
哪个效果很好! 但是,如果传递给script
的块尝试调用script
上不可用的方法 , 则可以调用Head上的方法 。 例如,
head { script { script { +"alert('hi');" } } }
不仅不是一个编译错误,它实际上相当于
head { script { } script { +"alert('hi');" } }
从模板作者的角度来看,这是超级混淆的。
有什么办法来防止方法查找像这样的范围内旅行? 我只希望它看到最内层的范围。
更新11/24/2016:Kotlin 1.1-M03引入了范围控制,我相信这正是解决这个问题的方法。 https://blog.jetbrains.com/kotlin/2016/11/kotlin-1-1-m03-is-here/
目前的行为是故意的。 lambda中的代码可以访问所有封闭作用域的接收者。 未来版本的Kotlin可能会添加一个修改器,将接收器的lambda限制为仅在该接收器上调用方法,而不是封闭的范围,但在当前版本中,无法更改该行为。
作为一个解决方法,我可以让它在运行时失败,如果我改变类看起来像这样:
open class Tag { operator fun String.unaryPlus() // pulled up from TagWithChildren, call protected method fun head(init: Head.() -> Unit) = addChild(Head()) fun script(init: Script.() -> Unit) = addChild(Head()) // throws in Tag open protected fun addChild(t: Tag) = throw IllegalArgumentException() } class TagWithChildren : Tag() { // overridden to not throw in subclass protected override fun addChild(t: Tag) = children.add(t) }
这样, 每个 Tag都具有构建器方法(解决范围问题),但实际调用它们可能会导致运行时失败。