为什么在使用 for-each 时循环变量实际上是最终的?

Why is the loop variable effectively final when using for-each?

    private void m10(String[] arr) {
        for (String s : arr) {
            Supplier<String> supplier = () -> {
                System.out.println(s);
                return null;
            };
            supplier.get();
        }
    }

    private void m10(Object[] arr) {
        for (Object s : arr) {
            Supplier<String> supplier = () -> {
                System.out.println(s);
                return null;
            };
            supplier.get();
        }
    }
    private void m11(String[] arr) {
        for (int i = 0; i < arr.length; i++) {
            Supplier<String> supplier = () -> {
                System.out.println(arr[i]);
                return null;
            };
            supplier.get();
        }
    }

在情况 2 中,我知道变量 i 不是有效的最终变量,因为它的值在循环迭代之间发生了变化。但是我不明白为什么 lambda 可以在情况 1 中工作。

s 永远不会改变 (s = ...)。所以编译器说 "yeah, we could theoretically mark this as final"。这就是 effectively final 的意思。 IE。你没有标记它 final 但你可以并且它仍然可以编译。

如果您想了解增强的 for 循环:

for (String s : arr)

变量不在 for 的范围之外,也不会被重新分配。 IE。它是不是:

String s = null;
for (int i = 0; i < arr.length; i++) {
    s = arr[i];
    ...
}

变量是在循环内部创建的,所以它的范围仅限于循环。它不会被重新使用,而是被丢弃并在每次迭代时重新创建:

for (int i = 0; i < arr.length; i++) {
    String s = arr[i];
    ...
}

仔细看看这两个例子。首先,你不能写 final String s = null;,因为我们在循环 s = arr[i]; 期间重新分配它。但是在第二个例子中我们可以,因为变量 s 只在一次迭代中已知,然后又被丢弃了。所以 final String s = arr[i]; 没问题。

作为旁注,这也解释了为什么不能在循环后使用 s。它是未知的并且已经销毁了,它的范围仅限于循环。

因为 s 的范围是单次迭代,所以没有一个 s 改变值,但每个循环迭代都有一个有效的最终 s

这就像用下面的方式编写你的案例 2,它编译

private void m11(String[] arr) {
    for (int i = 0; i < arr.length; i++) {
        String s = arr[i];
        Supplier<String> supplier = () -> {
            System.out.println(s);
            return null;
        };
        supplier.get();
    }
}