为什么在使用 for-each 时循环变量实际上是最终的?
Why is the loop variable effectively final when using for-each?
- 情况一:使用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();
}
}
- 情况 2:它会捕获编译时错误
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();
}
}
- 情况一:使用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();
}
}
- 情况 2:它会捕获编译时错误
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();
}
}