2010-10-12 88 views
59

我很惊讶地看到,下面的Java代码片断编译并运行:“final int i”如何在Java for循环中工作?

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

其中listOfNumbers是一个整数数组。

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

回答

66

试想一下,速记看起来像这个有很多:

for (Iterator<Integer> iter = listOfNumbers.iterator(); iter.hasNext();) 
{ 
    final int i = iter.next(); 
    { 
     System.out.println(i); 
    } 
} 
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(); 
     } 
    } 
} 

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

顺便说一句,大约10年前,你可能已经看到在局部变量上使用final的速度非常小(在一些极少数情况下)。很长一段时间情况并非如此。现在使用final的唯一原因是允许你使用这样的词法关闭。

在回答@mafutrct:

当你写的代码,你有两个观众这样做。首先是计算机,只要它在语法上是正确的,就会对你做出的任何选择感到满意。第二个是面向未来的读者(通常是你自己),这里的目标是传达代码的“意图”,而不会遮掩代码的“功能”。应尽可能少地使用语言特征,以减少歧义。

在循环变量的情况下,可以使用final来传达以下两件事之一:单一赋值;或者关闭。循环的简单扫描会告诉你循环变量是否被重新分配;但是,由于在创建闭包时交织了两个执行路径,可能很容易错过用于捕获变量的闭包。当然,除非你只使用final来指示捕获的意图,在这一点上,读者就会明白正在发生的事情。

+7

或者记录不使该变量可重新分配的意图。 – 2010-10-12 04:52:53

+2

不!如果你这样做,你现在必须为语义相同的语义 - 一个有实际用途;另一个是多余的,除非你的方法太长太久了。只使用final来表示您打算关闭参数,_never_表示单个分配。 – Recurse 2010-10-15 04:09:20

+0

@Recurse这听起来很合理。你能否解释它背后的基本原理?我不确定我了解原因 – mafu 2012-11-27 14:10:57