Rik Schaaf
Rik Schaaf

Reputation: 1183

Using class field in lambda as parameter Scala

I am trying to create a constructor in which one of the methods could be passed. See my code:

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()
  }
}

The in this example val test1 = x does work, because it is a parameter of the Bar constructor, but val test2 = y does not work, even though Bar extends Foo and Foo has a field named y.

So my question is: why do you not have access to the y of the Foo class variable from within the function in Bar


EDIT:

When reading the answer, also look at the first comment by Matheusz Kubuszok and http://ideone.com/9yMpvw vs http://ideone.com/0fDxXe. The second link is the reason why the code in the first link should also fail. I imagine that edge case detection like this would make the compiler way more complex so I now indeed understand why they chose not to allow it.

Upvotes: 2

Views: 555

Answers (1)

Mateusz Kubuszok
Mateusz Kubuszok

Reputation: 27595

Basically your code is equivalent to something like:

val barFun(x: Int) = () => {
  var test1 = x
  var test2 = y
  println(x + ", " + y)
}

class Bar(x: Int) extends Foo(barFun(x), x, 50)

When you are creating your lambda, it only sees arguments passed into constructor - it is created within the scope of a constructor, so it has access to variables passed into it as a closure. It does not have access to Foo class as it is not its enclosing class. You can check it if you do something like:

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

You'll see that lambda will have no access to neither x nor y. Other test I tried in ammonite shows this:

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

As a matter of the fact, this makes sens. At the moment you are creating the lambda, class is not yet initialized. If whatever you do during class initialization had access to all those uninitialized vars, things could turn nasty.

Upvotes: 3

Related Questions