为什么在java中的for-each循环中需要声明变量

Sti*_*tim 52 java syntax foreach

每个循环的通常形式是:

for(Foo bar: bars){
    bar.doThings();
}
Run Code Online (Sandbox Code Playgroud)

但是如果我想在循环之后保留bar,我就不能使用for循环:

Foo bar = null;
// - Syntax error on token "bar", Identifier expected after this token
for(bar: bars){ 
    if(bar.condition())
        break;
}
bar.doThings();
Run Code Online (Sandbox Code Playgroud)

for循环得到上面提到的语法错误.

为什么是这样? 我对解决方法不感兴趣,但只是对此限制背后的考虑因素感到好奇.

相反,对于普通的for循环,变量可以在外部声明或根本不声明...

int i = 1;
for(;i<max;i++){
    for(;;){
        // Do things
    }
}
Run Code Online (Sandbox Code Playgroud)

Sor*_*row 29

这是一个很好的问题,我很乐意看到一些深入的答案.但是,官方文件说:

这些缺点是设计师所熟知的,他们有意识地决定采用干净,简单的结构来覆盖绝大多数情况.

绝大多数案件都是我的答案.

另外,就个人而言,我认为foreachJava 中的循环只是标准迭代器循环的一个很好的语法.因此,编译器为结构创建迭代器,并使用该变量获取当前迭代的值.为了确保变量已经初始化,你需要为循环范围声明它(我认为这可以防止变量在其他地方使用,例如在另一个线程中).因此,您不能在循环后使用该变量.但是,这只是我的观点,我很乐意听到更了解它的人的消息.:)

编辑这是一篇关于foreachJava循环的有趣文章.

我分析的另一个编辑(使用jclasslib这些方法的字节码:

 private static void testForEach(ArrayList<String> als) {
  for(String s: als)
    System.out.println(s);
 }

 private static void testIterator(ArrayList<String> als) {
  for(Iterator<String> is = als.iterator(); is.hasNext();) {
   String s = is.next();
   System.out.println(s);
  } 
 }
Run Code Online (Sandbox Code Playgroud)

两种方法都由相同的字节码表示:

 0 aload_0
 1 invokevirtual #2 <java/util/ArrayList.iterator>
 4 astore_1
 5 aload_1
 6 invokeinterface #3 <java/util/Iterator.hasNext> count 1
11 ifeq 34 (+23)
14 aload_1
15 invokeinterface #4 <java/util/Iterator.next> count 1
20 checkcast #5 <java/lang/String>
23 astore_2
24 getstatic #6 <java/lang/System.out>
27 aload_2
28 invokevirtual #7 <java/io/PrintStream.println>
31 goto 5 (-26)
34 return
Run Code Online (Sandbox Code Playgroud)

差异在第1行,后一种方法使用invokevirtual #8.但是,两次调用都会导致调用相同的方法Iterator.因此,foreach正如文档所述,它似乎只是编译器转换为预定义构造的语法糖.这并没有回答为什么它是这样的问题.我认为这很有趣,在讨论的背景下可能值得一提.

  • 有趣的是,我在这里提到的情况并没有被列为*这些缺点之一*.他们只提到需要直接访问迭代器的缺点...... (3认同)