2017-04-09 40 views
2

我想創建一個構造函數,其中的一個方法可以傳遞。見我的代碼:在lambda中使用類字段作爲參數斯卡拉

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延伸FooFoo有一個名爲y場。

所以我的問題是:你爲什麼沒有從函數內Bar


編輯訪問Foo類變量的y

當讀答案,也請看Matheusz Kubuszok的第一條評論和http://ideone.com/9yMpvwhttp://ideone.com/0fDxXe。第二個鏈接是第一個鏈接中的代碼也應該失敗的原因。我想像這樣的邊緣情況檢測會使編譯器的方式更復雜,所以我現在確實明白他們爲什麼選擇不允許它。

回答

3

基本上你的代碼就相當於是這樣的:

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

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

當你創建你的拉姆達,它只關注傳遞到構造函數的參數 - 這是一個構造函數的範圍內創建的,所以它可以訪問作爲閉包傳遞給它的變量。它不是而不是有權訪問Foo類,因爲它不是它的封閉類。您可以檢查它,如果你這樣做:

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

你會看到拉姆達必須既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 

事實上,這使得sens。在您創建lambda的那一刻,類尚未初始化。如果你在課堂初始化過程中所做的任何事情都可以訪問所有未初始化的東西,那麼情況就會變得糟糕。

+0

感謝您澄清爲什麼它不起作用。儘管如此,它仍令我困惑。在你最後一段中,你解釋了爲什麼它有意義,但是如果lambda是在類構造函數內部創建的,爲什麼'var's會被初始化?我認爲構造函數首先調用超類構造函數,該構造函數初始化該類的變量,然後才執行構造函數本身,因此任何對lambda的使用都會在'var'初始化之後出現。 –

+0

或者是因爲lambda是一個參數而不是構造函數體的一部分? –

+1

種類。假設lambda可以訪問類參數。我們可以定義它,然後像'{val z = x + y; ()=> println(z)}'。它會在'y'中存儲'x'並將其存儲爲'z',然後創建lambda,它會打印'z'而不會每次計算它。除了'Foo.x'和'Foo.y'尚未初始化,因爲我們在參數傳入構造函數之前調用它們。參數下面是getters返回某個內部變量的值。在調用構造函數之前,兩者都是'null'。所以我們以'val z = null + null'結束。構造函數調用時出現'NullPointerException'。 –