2017-10-05 41 views
0

我明白这是函数式编程的标准部分..我的问题是为什么编译器不能在lambda语句开始之前自动声明变量的副本为final?Java:为什么java不能在包含lambda表达式的范围中自动“终结”局部变量?

import java.util.stream.IntStream; 

public class Example 
{ 
    public static void main(String args[]) 
    { 
    int i = 5; 
    i = 6; 
    IntStream.range(0, 10).mapToLong(j-> i * j).sum(); 
    } 


} 

失败......与“我在封闭范围内定义必须是最后的或有效的最终局部变量”,而这似乎编译器应该足够聪明,做这样的事情

import java.util.stream.IntStream; 

public class Example 
{ 
    public static void main(String args[]) 
    { 
    int i = 5; 
    i = 6; 
    final int _i = i; 
    IntStream.range(0, 10).mapToLong(j-> _i * j).sum(); 
    } 


} 

的编译器可以强制终结变量永远不会被lambda函数修改

+3

它可以;它会让代码更难以推理。 –

+0

会使它在多线程场景中难以使用和理解(这也从总决赛中受益)。 –

+0

@AdamKotwasinski多线程如何改变任何东西?我不建议lambda函数能够修改其父范围中的变量 – crow

回答

0

但如果拉姆达是其中的某处使用它异步传递什么(当前函数结束后,即它可以运行),并修改变量i在功能范围创建拉姆达之后?

int i = 5; 
i = 6; 
useLambdaAsynchronously(j-> i * j); 
i = 7; 

拉姆达仍然会捕获i一个值6,但i(这应该是相同的变量,因为你只申报一个i)目前拥有7在其他范围的值。这是不一致的,因为程序员应该期望一个变量一次只有一个值。如果稍后运行lambda,则即使先前已将7指派给i,它仍将使用i的值6。

为了避免这个问题,编译器需要确保在lambda表达式中未分配变量,在创建lambda之后,原始函数范围内没有指定该变量。但是这会导致在一个函数中允许更早的赋值,但后来在同一个函数中被禁止(仅仅因为它已经被lambda捕获),这对程序员来说也可能是令人惊讶的。为了简单起见,Java只是不允许在任何地方分配任务。

0

那么,编译器实际上这样做,如果您的变量i是有效的最终。

public static void main(String args[]) 
    { 
    int i = 5; 
    IntStream.range(0, 10).mapToLong(j-> i * j).sum(); 
    } 

但是与第二次分配i = 6;你变得无法“有效的最后”,就表示你确实希望它是可变的。

那么,为什么在这种情况下,编译器会尽可能地发出变量的最终副本,尽管您发信号表示希望它是可变的?

+0

编译器应该知道变量在封闭范围中的lambda表达式之后没有被修改,也没有在lambda表达式中被修改,因此应该生成必要的字节代码,这样我的解决方法加入“final int _a = a;”就在lambda表达式没有必要之前。锅炉板代码很糟糕,而且它已成为最近Java版本的方向,因此不需要使用详细和样板构造 – crow

相关问题