Java:为什么java不能在包含lambda表达式的范围中自动“终结”局部变量?
我明白这是函数式编程的标准部分..我的问题是为什么编译器不能在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函数修改
但如果拉姆达是其中的某处使用它异步传递什么(当前函数结束后,即它可以运行),并修改变量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只是不允许在任何地方分配任务。
那么,编译器将实际上这样做,如果您的变量i
是有效的最终。
public static void main(String args[])
{
int i = 5;
IntStream.range(0, 10).mapToLong(j-> i * j).sum();
}
但是与第二次分配i = 6;
你变得无法“有效的最后”,就表示你确实希望它是可变的。
那么,为什么在这种情况下,编译器会尽可能地发出变量的最终副本,尽管您发信号表示希望它是可变的?
编译器应该知道变量在封闭范围中的lambda表达式之后没有被修改,也没有在lambda表达式中被修改,因此应该生成必要的字节代码,这样我的解决方法加入“final int _a = a;”就在lambda表达式没有必要之前。锅炉板代码很糟糕,而且它已成为最近Java版本的方向,因此不需要使用详细和样板构造 – crow
它可以;它会让代码更难以推理。 –
会使它在多线程场景中难以使用和理解(这也从总决赛中受益)。 –
@AdamKotwasinski多线程如何改变任何东西?我不建议lambda函数能够修改其父范围中的变量 – crow