在lambda中使用类字段作为参数Scala



我正在尝试创建一个可以传递其中一个方法的构造函数。查看我的代码:

class Foo(fooMethod: () => Unit, var x: Int, var y: Int) {
  def foo() = fooMethod()
}
class Bar(x: Int) extends Foo(() => {
  var test1 = x
  var test2 = y
  println(x + ", " + y)
}, x, 50)

class FieldDemo {
  def main(args: Array[String]): Unit = {
    val bar = new Bar(40)
    bar.foo()
  }
}

此示例中的 val test1 = x 确实有效,因为它是 Bar 构造函数的参数,但val test2 = y不起作用,即使Bar扩展Foo并且Foo具有一个名为 y 的字段。

所以我的问题是:为什么您不能从函数中访问 Foo 类变量的y Bar

<小时 />

编辑

在阅读答案时,还要看看Matheusz Kubuszok的第一条评论和 http://ideone.com/9yMpvw vs http://ideone.com/0fDxXe。第二个链接是第一个链接中的代码也应该失败的原因。我想像这样的边缘情况检测会使编译器更加复杂,所以我现在确实明白为什么他们选择不允许它。

基本上你的代码等效于这样的东西:

val barFun(x: Int) = () => {
  var test1 = x
  var test2 = y
  println(x + ", " + y)
}
class Bar(x: Int) extends Foo(barFun(x), x, 50)

当您创建 lambda 时,它只看到传递到构造函数中的参数 - 它是在构造函数范围内创建的,因此它可以访问作为闭包传入的变量。它无法访问 Foo 类,因为它不是它的封闭类。如果您执行以下操作,则可以检查它:

class Bar(z: Int) extends Foo(() => {
  var test1 = x
  var test2 = y
  println(x + ", " + y)
}, z, 50)

您将看到 lambda 既无法访问x也无法访问y。我在菊石中尝试过的其他测试表明了这一点:

class Bar(z: Int) extends Foo(() => {
  var test1 = Foo.this.x
  var test2 = Bar.this.y
}, z, 50)
cmd1.sc:2: Foo is not an enclosing class
  var test1 = Foo.this.x
              ^
cmd1.sc:3: Bar is not an enclosing class
  var test2 = Bar.this.y
              ^
Compilation Failed

事实上,这是有道理的。 在创建 lambda 的那一刻,类尚未初始化。如果您在类初始化期间所做的任何事情都可以访问所有这些未初始化的var,那么事情可能会变得令人讨厌。

最新更新