在嵌套的闭包中使用了错误的“this”

时间:2016-01-17 03:16:03

标签: kotlin

我试图保持这个最小化,但如果我太小,请告诉我。

假设你有一个像这样的类层次结构,用于生成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() 使用Headhead方法,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/

2 个答案:

答案 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)
}

这样,每个标签都有构建器方法(解决了作用域问题),但实际调用它们可能会导致运行时失败。