Java-在lambda中更改最终变量的值

Java-在lambda中更改最终变量的值,java,foreach,lambda,anonymous-inner-class,Java,Foreach,Lambda,Anonymous Inner Class,在Java中,我有以下代码 List<Integer> myList = new ArrayList<>(); for (int i=0;i<9;i++) { myList.add(i); } Integer sum = 0; myList.forEach(i -> { sum = sum + i; // does not compile, sum needs to be final or effectively final });

在Java中,我有以下代码

List<Integer> myList = new ArrayList<>();
for (int i=0;i<9;i++) {
    myList.add(i);
}

Integer sum = 0;

myList.forEach(i -> {
    sum = sum + i; // does not compile, sum needs to be final or effectively final
});   

for(int i : myList) {
    sum = sum + i; //runs without problems
} 
List myList=new ArrayList();
对于(int i=0;i{
sum=sum+i;//未编译,sum需要是final或final
});   
for(int i:myList){
sum=sum+i;//运行时没有问题
} 
我的问题是,为什么我不能从lambda中改变sum的值?它的功能与下面的for循环完全相同,还是我错了?有趣的是,如果我将main方法之外的整数和声明为static,那么它也可以工作。谁能解释一下原因吗

编辑:在另一个类似的问题中,答案似乎是:

它是向后兼容性和项目资源约束的组合

然而,我仍然无法理解,如果我将sum设置为一个数组,或者如果我在main之外声明它,为什么它会起作用。我还想了解myList.forEach和下面的for循环之间的区别,为什么一个有效而另一个无效。

试试:

final Integer[] sum = new Integer[1];
sum[0] = 0;

myList.forEach(i -> {
    sum[0] = sum[0] + i; // does not compile, sum needs to be final or effectively final
}); 
因为lambda实际上是初始化匿名类(和重写方法)的语法糖

这和你写的一样:

final Integer[] sum = new Integer[1];
sum[0] = 0;

myList.forEach(new Consumer() {
    public void accept(Integer element) {
        sum[0] = sum[0] + element;
    }
});
来自外部作用域且在内部作用域中使用的变量必须是final(在本例中为
sum
)。这仅仅是因为Java不支持闭包。因此,外部变量必须标记为final。由于Integer本身是不可变的(如果您将其声明为final,则不能再对其进行更改),所以必须使用包装器对象或数组(就像我所做的那样)

您可以在此处找到更多有用的信息:


这并不是您想要的答案,但在大多数情况下,您不需要在lambda中修改它。这是因为lambdas不习惯以适当的功能样式进行状态更改

您可以使用提供的任何高级函数屏蔽“累加器”,然后分配:

sum = myList.stream().mapToInt(x->x).sum();

lambda基本上是一个匿名类。只能从匿名类访问最终局部变量

您需要的是一个可以修改其内容的包装器类。对于快速破解,您可以在本例中使用
AtomicInteger

AtomicLong sum = new AtomicLong(0);
myList.forEach(i -> {
  sum.addAndGet(i); // does not matter getAndAdd or addAndGet
});

sum.get(); // to get the value

另一种方法是,如果您使用Intellij IDEA,IDE可以建议您将变量转换为最终的一个元素数组(如darijan的回答所示)。

此外,由于您运行的是Java 8,您可能需要考虑执行
myList.stream().forEach
。看,这并不能真正回答问题。它是有效的,但背后的原因是什么?你能详细解释一下这种行为吗?Thanks@MickMnemonic我不同意。它确实有助于记忆,这就是我改变它的原因。谢谢你指出这一点。