2017-02-04 64 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状态的变化是一个副作用。 –