我很惊讶地发现以下Java代码片段已编译并运行:
for(final int i : listOfNumbers) {
System.out.println(i);
}
Run Code Online (Sandbox Code Playgroud)
其中listOfNumbers是一个整数数组.
我认为最终的声明只分配了一次.编译器是否创建了Integer对象并更改了它引用的内容?
Tra*_*kel 66
想象一下,速记看起来很像这样:
for (Iterator<Integer> iter = listOfNumbers.iterator(); iter.hasNext(); )
{
final int i = iter.next();
{
System.out.println(i);
}
}
Run Code Online (Sandbox Code Playgroud)
Rec*_*rse 14
有关所涉及的范围规则的说明,请参阅@TravisG.您将使用这样的最终循环变量的唯一原因是您需要在匿名内部类中关闭它.
import java.util.Arrays;
import java.util.List;
public class FinalLoop {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(new Integer[] { 0,1,2,3,4 });
Runnable[] runs = new Runnable[list.size()];
for (final int i : list) {
runs[i] = new Runnable() {
public void run() {
System.out.printf("Printing number %d\n", i);
}
};
}
for (Runnable run : runs) {
run.run();
}
}
}
Run Code Online (Sandbox Code Playgroud)
循环变量在执行时不在Runnable的范围内,仅在实例化时才变量.如果没有final关键字,则Runnable最终运行时,循环变量将不可见.即使它是,它对所有Runnables都是相同的值.
顺便说一句,大约10年前,你可能已经看到在本地变量上使用final的速度非常小(在极少数情况下); 这种情况很长一段时间都不是这样.现在使用final的唯一原因是允许你使用像这样的词法闭包.
回答@mafutrct:
编写代码时,您可以为两个受众执行此操作.第一个是计算机,只要它在语法上是正确的,就会对你做出的任何选择感到满意.第二个是未来的读者(通常是你自己),这里的目标是传达代码的"意图",而不会模糊代码的"功能".语言特征应尽可能少地使用,以减少歧义.
在循环变量的情况下,final的使用可用于传达两种事物中的任何一种:单一赋值; 或者,关闭.循环的简单扫描将告诉您是否重新分配了循环变量; 但是,由于在创建闭包时交错了两个执行路径,因此很容易错过闭包旨在捕获变量.当然,除非你只使用final来表示捕获意图,否则读者会明白发生了什么.