让我们想象一下,我需要一个正方形列表:
public static List<Integer> test1() {
int i = 0;
return IntStream.iterate(0, sqrt -> sqrt < 100, sqrt -> { ++i; return i*i; })
.boxed().collect(Collectors.toList());
}
它会失败
错误:从 lambda 表达式引用的局部变量必须是最终的或实际上最终的
是的,我知道,有很多更好的方法可以达到相同的结果。其一:
public static List<Integer> test2() {
return IntStream.iterate(0, i -> i < 10, i -> i+1).map(i -> i * i)
.boxed().collect(Collectors.toList());
}
当我想引用 lambda 中的局部变量时,我能够重写所有情况。 但我还是很好奇。
我不需要任何帮助来修复此特定代码。我什至认为你不应该在实际代码中做这样的事情。这是一个严格的理论问题:
如果我被迫这样做,应该如何做?
嗯...我真的会被迫这样做吗?
(我的意思是更改 Java lambda 中的外部变量。)
目前我知道 3 种更改 lambda 中外部变量的方法:
将其包装在数组中:
public static List<Integer> test3() { int[] i = {0} return IntStream.iterate(0, sqrt -> sqrt < 100, sqrt -> { ++(i[0]); return i[0]*i[0]; }) .boxed().collect(Collectors.toList()); }
像这里 https://www.baeldung.com/java-lambda-effectively-final-local-variables
这很丑。将局部变量转换为实例变量。
这不仅丑陋,而且不安全。 (相同的href)- 使用
AtomicInteger
(或类似的)。
喜欢这里Forcing Java lambda expressions to capture non-final variables in Java
这有点矫枉过正了
我读过的所有其他问题的所有其他答案都只提出了这三种我认为不令人满意的解决方案,或者它们处理的是常量而不是可变变量。喜欢这里How to use non final variable in Java 8 Lambdas
有什么建议吗?
最佳答案
您可以使用Stream.iterate()
方法来提供自定义 seed
对象,该对象将在您的迭代中使用。您可以使用所需的所有状态信息填充该对象,并从实际迭代函数/方法/lambda 内部访问它。它可以看起来像这样:
状态对象:
public class SeedObject {
public final int state;
public final int value;
public SeedObject(int state, int value) {
this.state = state;
this.value = value;
}
@Override
public String toString() {
return this.state+"|"+this.value;
}
}
在新类中使用 iterate()
方法。
public static void main(String[] args) throws Exception{
List<SeedObject> result = Stream.iterate(new SeedObject(1,1), last -> {
int next = last.state+1;
return new SeedObject(next, next*next);
})
.limit(10)
.collect(Collectors.toList());
System.out.println(result);
}
这将产生以下输出:
[1|1, 2|4, 3|9, 4|16, 5|25, 6|36, 7|49, 8|64, 9|81, 10|100]
从那里,您可以根据需要转换列表和/或从列表的最后一个元素读取最后的状态。这样,您的迭代和 lambda 就会在 iterate()
调用的“内部”状态,并且无法访问“外部”。
关于java - 如何更改 Java lambda 中的外部非最终变量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59595637/