gpt4 book ai didi

java - 增强的 'for' 循环和 lambda 表达式

转载 作者:IT老高 更新时间:2023-10-28 20:53:50 25 4
gpt4 key购买 nike

据我了解,lambda 表达式捕获的是值,而不是变量。例如,以下是编译时错误:

for (int k = 0; k < 10; k++) {
new Thread(() -> System.out.println(k)).start();
// Error—cannot capture k
// Local variable k defined in an enclosing scope must be final or effectively final
}

但是,当我尝试使用增强的 for-loop 运行相同的逻辑时,一切正常:

List<Integer> listOfInt = new Arrays.asList(1, 2, 3);

for (Integer arg : listOfInt) {
new Thread(() -> System.out.println(arg)).start();
// OK to capture 'arg'
}

为什么增强的 for 循环可以正常工作,而不是普通的常规 for 循环,尽管增强的 for 循环也是内部某处通过正常循环递增变量。**

最佳答案

Lambda 表达式的工作方式类似于回调。在将它们传递到代码中的那一刻,它们“存储”了它们操作所需的任何外部值(或引用)(就好像这些值在函数调用中作为参数传递一样。这只是对开发人员隐藏)。在您的第一个示例中,您可以通过存储 k 来解决该问题。到一个单独的变量,如 d:

for (int k = 0; k < 10; k++) {
final int d = k
new Thread(() -> System.out.println(d)).start();
}

有效final意思是,在上面的例子中,你可以省略'final'关键字,因为d实际上是最终的,因为它在其范围内永远不会改变。

for循环的操作方式不同。它们是迭代代码(与回调相反)。它们在各自的范围内工作,并且可以使用自己堆栈上的所有变量。这意味着,for循环的代码块是外部代码块的一部分。

关于您突出显示的问题:

增强的 for循环不使用常规索引计数器操作,至少不是直接操作。增强 for循环(在非数组上)创建一个隐藏的迭代器。您可以通过以下方式进行测试:

Collection<String> mySet = new HashSet<>();
mySet.addAll(Arrays.asList("A", "B", "C"));
for (String myString : mySet) {
if (myString.equals("B")) {
mySet.remove(myString);
}
}

以上示例将导致 ConcurrentModificationException。这是由于迭代器注意到底层集合在执行期间发生了变化。但是,在您的示例中,外部循环创建了一个“有效地最终”变量 arg可以在 lambda 表达式中引用,因为该值是在执行时捕获的。

防止捕获“非有效最终”值或多或少只是 Java 中的一种预防措施,因为在其他语言(例如 JavaScript)中,这种方式的工作方式不同。

因此,编译器理论上可以翻译您的代码、捕获值并继续,但它必须以不同的方式存储该值,您可能会得到意想不到的结果。因此,为 Java 8 开发 lambdas 的团队正确地排除了这种情况,通过异常阻止它。

如果您需要在 lambda 表达式中更改外部变量的值,您可以声明一个单元素数组:

String[] myStringRef = { "before" };
someCallingMethod(() -> myStringRef[0] = "after" );
System.out.println(myStringRef[0]);

或使用 AtomicReference<T>使其成为线程安全的。但是,对于您的示例,这可能会返回“之前”,因为回调很可能在 println 执行之后执行。

关于java - 增强的 'for' 循环和 lambda 表达式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54340101/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com