“final int i”如何在Java for循环中工作?

2022-08-31 16:27:33

我很惊讶地看到下面的Java代码片段编译并运行:

for(final int i : listOfNumbers) {
     System.out.println(i);
}

其中 listOfNumbers 是整数数组。

我以为最终的声明只分配了一次。编译器是否正在创建 Integer 对象并更改它引用的内容?


答案 1

想象一下,速记看起来很像这样:

for (Iterator<Integer> iter = listOfNumbers.iterator(); iter.hasNext(); )
{
    final int i = iter.next();
    {
        System.out.println(i);
    }
}

答案 2

有关所涉及的范围界定规则的说明,请参阅@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();
        }
    }
}

循环变量在执行时不在 Runnable 的范围内,只有在实例化时才在 Runnable 的范围内。如果没有关键字,循环变量在最终运行时对 Runnable 不可见。即使它是,对于所有Runnables来说,它也是相同的值。final

顺便说一句,大约10年前,您可能已经看到在局部变量上使用final的速度提高了很小(在一些罕见的情况下);这种情况已经很久没有了。现在,使用 final 的唯一原因是允许您使用这样的词法闭包。

在回答@mafutrct:

编写代码时,是为两个受众编写的。第一种是计算机,只要它在语法上是正确的,它就会对你所做的任何选择感到满意。第二个是对于未来的读者(通常是你自己),这里的目标是传达代码的“意图”,而不会模糊代码的“功能”。语言特征的使用习惯应尽可能少,以减少歧义。

在循环变量的情况下,使用 final 可用于传达以下两种情况之一:单赋值;或者,关闭。对循环的简单扫描将告诉您循环变量是否被重新分配;但是,由于在创建闭包时两个执行路径交错,因此很容易错过闭包旨在捕获变量的情况。当然,除非你只使用final来表示意图捕获,否则此时读者就会清楚地知道发生了什么。


推荐