【问题标题】:Java - changing the value of a final variable from within a lambdaJava - 从 lambda 中更改最终变量的值
【发布时间】:2015-10-29 08:57:37
【问题描述】:

在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 循环完全相同,还是我错了?有趣的是,如果我将主方法之外的整数和声明为静态,它也可以工作。谁能解释我为什么?

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

它是向后兼容性和项目资源限制的结合。

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

【问题讨论】:

标签: java foreach lambda anonymous-inner-class


【解决方案1】:

尝试:

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;
    }
});

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

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

【讨论】:

  • 这并不能真正回答问题。
  • 它有效,但它背后的原因是什么?你能详细解释一下这种行为吗?谢谢
  • @MickMnemonic 我不同意。它确实回答了。
  • @MickMnemonic 这就是我改变它的原因。感谢您指出这一点。
【解决方案2】:

不完全是您正在寻找的答案,但在大多数情况下,您不需要在 lambda 中修改它。这是因为 lambda 以适当的函数风格改变状态并不符合习惯。

你可以做的是使用提供的任何更高级别的函数来屏蔽“累加器”,然后赋值:

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

【讨论】:

    【解决方案3】:

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

    您需要的是一个可以修改其内容的包装类。对于快速破解,您可以在这种情况下使用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 的回答)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-02-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-06-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多