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

标签 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
} 

我的问题是,为什么我无法从 lambda 中更改 sum 的值?它的作用与下面的 for 循环完全相同,还是我错了?有趣的是,如果我在 main 方法之外将整数和声明为静态,它也能正常工作。谁能解释我为什么?

编辑:在另一个类似的问题中 Does Java 8 support closures ,答案似乎是:

it is a combination of backwards compatibility and project resourcing constraints.

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

最佳答案

尝试:

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

myList.forEach(i -> {
    sum[0] = sum[0] + i;
}); 

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

这和你写的一样:

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

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

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

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

关于Java - 从 lambda 中更改最终变量的值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31851892/

相关文章:

c# - 获取作为参数发送的变量的名称

javascript - 如何创建一个函数并传入可变长度参数列表?

lambda - 使用 lambda/arrow 函数的 TypeScript 抽象方法

java - 方法体的参数列表怎么可能有一个类?

java - string.replaceAll 切割字符 50% 的时间

java - jar导出后找不到类

mysql - 比较行并选择最大值

javascript - 以编程方式确定是否可以在对象上运行 for 循环的最佳解决方案

java - Spring 启动 : Getting @Scheduled cron value from database

c++ - 不取消引用迭代器的 std::for_each 等价物