2017-02-04 65 views
-2

給了C#代碼:比較拉姆達捕獲C#和Java

int x = 10; 

Func<int> next =() => { return ++x; }; 

Console.WriteLine("Before lambda, x = {0}", x); 
Console.WriteLine("The successor of {0} is {1}", x, next()); 
Console.WriteLine("After lambda, x = {0} \n", x); 

輸出是:

Before lambda, x = 10 
The successor of 10 is 11 
After lambda, x = 11 

拉姆達後,x的值是受拉姆達行動的話,這相當於通過引用將參數傳遞給函數。

讓我們與Java比較:

interface Inc 
{ 
    int Increment(); 
} 

int x = 10; 
Inc next =() -> { return ++x; }; 

System.out.println("Before lambda, x = " + x); 
System.out.println("The successor of " + x + " is " + next.Increment()); // ERROR: local variables referenced from a lambda expression must be final or effectively final 
System.out.println("After lambda, x = " + x + "\n"); 

如果我改變拉姆達這樣:

Inc next =() -> { return x+1; }; 

輸出是:

Before lambda, x = 10 
The successor of 10 is 11 
After lambda, x = 10 

拉姆達後,x的值不受lambda動作的影響,所以這相當於通過值將參數傳遞給函數。

阻止Java更改捕獲的變量的技術原因是什麼?

我知道錯誤消息說的話:

«從lambda表達式中引用的局部變量必須是最後的或有效的最後»

但這是不夠的我。這背後的技術原因是什麼?

如果Java允許捕獲的變量可能被lambda更改,結果會是什麼?

謝謝。

回答

0

Java和JVM不支持對局部變量的引用。編譯器可以做的是將該變量轉換爲引用,但不是。

例如它可以像這樣翻譯你的代碼。

int[] x = { 10 }; 
IntSupplier next =() -> { return ++x[0]; }; 

System.out.println("Before lambda, x = " + x); 
System.out.println("The successor of " + x + " is " + next.getAsInt()); // 
System.out.println("After lambda, x = " + x + "\n"); 

如果你真的需要這個,你可以自己改變代碼。

順便說一句功能編程的一個關鍵特性是儘可能使用純函數。純功能沒有副作用。雖然Java沒有任何強制執行純函數的方法,但iMHO似乎增加了一種微妙副作用的方式,與添加功能性結構並不一致。

+0

我認爲這與副作用無關,因爲變量是由值捕獲的,所以在lambda裏面我們有一個變量的副本。 –

+1

@ArnbjorgJohansen準確地說,你有一個*副本*的變量,這意味着你不能更新原件。爲了防止你錯誤地認爲更新副本會更新原文,語言設計者做出了一個偉大的決定,要求變量被*(有效)* final(不可變),所以你不能*犯這個錯誤。 ---嗯......我想這就是答案*「這背後的技術原因是什麼?」* – Andreas

+0

@ArnbjorgJohansen狀態的變化是一個副作用。 –