我试图保持这个最小化,但如果我太小,请告诉我。
假设你有一个像这样的类层次结构,用于生成HTML(受Kotlin教程的启发;后面是半伪代码):
$query = "INSERT INTO order (foodid,name) VALUES ($food_id,'$food')";
$result = pg_query($conn,$query) or die("Query cannot be executed");
请注意,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
上无法使用的方法,则可以调用Head上的方法。例如,
Script
不仅不是编译错误,它实际上等同于
head {
script {
script {
+"alert('hi');"
}
}
}
从模板作者的角度来看,超级混乱。
有没有办法阻止方法查找这样的范围向上移动?我只希望看看最里面的范围。
2016年11月24日更新: Kotlin 1.1-M03引入了范围控制,我相信这正是解决了这个问题。 https://blog.jetbrains.com/kotlin/2016/11/kotlin-1-1-m03-is-here/
答案 0 :(得分:3)
目前的行为是故意的。 lambda中的代码可以访问所有封闭范围的接收者。未来版本的Kotlin可能会添加一个修饰符,它将带接收器的lambda限制为仅调用该接收器上的方法而不是封闭的范围,但在当前版本中,无法改变该行为。
答案 1 :(得分:2)
作为一种解决方法,如果我将类更改为如下所示,我可以在运行时失败:
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)
}
这样,每个标签都有构建器方法(解决了作用域问题),但实际调用它们可能会导致运行时失败。