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

Ale*_*ara 5 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
});   

for(int i : myList) {
    sum = sum + i; //runs without problems
} 
Run Code Online (Sandbox Code Playgroud)

我的问题是,为什么我不能在lambda中改变sum的值?它与下面的for循环完全相同,或者我错了?有趣的是,如果我将main方法之外的整数和声明为静态,它也可以.谁能解释我为什么?

编辑:在另一个类似的问题Java 8支持闭包,答案似乎是:

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

但是,我仍然无法理解为什么它可以工作,如果我将数组和数组或我在main之外声明它.我还想了解下面的myList.forEach和for循环之间有什么区别,为什么一个有效,另一个没有.

dar*_*jan 4

尝试使用:

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

myList.forEach(i -> {
    sum[0] = sum[0] + i;
}); 
Run Code Online (Sandbox Code Playgroud)

因为 lambda 实际上是一个用于初始化匿名类(并覆盖方法)的语法糖。

这就像你写的一样:

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

myList.forEach(new Consumer() {
    public void accept(Integer element) {
        sum[0] = sum[0] + element;
    }
});
Run Code Online (Sandbox Code Playgroud)

来自外部作用域并在内部作用域内使用的变量必须是最终变量(在本例中sum)。那只是因为 Java 不支持闭包。因此,外部变量必须标记为final。由于 Integer 本身是不可变的(如果将其声明为最终的,则无法再更改它),因此必须使用包装对象或数组(就像我所做的那样)。

您可以在这里找到更多有用的信息: