且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

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

更新时间:2021-08-15 22:00:49

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

This is a good question and I would be happy to see some in-depth answer. However, the official documentation says:



设计师知道这些缺点,他们做出了一个有意识的
决定采用干净简单的
构造可以覆盖大部分
的大部分案例。

These shortcomings were known by the designers, who made a conscious decision to go with a clean, simple construct that would cover the great majority of cases.

绝大多数案例都是我的答案。

The great majority of cases is the answer for me.

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

On a side note, personally, I think that foreach loop in Java is just a nice syntax for a standard iterator loop. So, the compiler creates the iterator for the structure and uses the variable to get the value for current iteration. To ensure that the variable has been initialised you need to declare it for the scope of loop (and I think this prevents the variable from being used somewhere else, e.g. in another thread). Because of that, you cannot use the variable after the loop. But, this is just my opinion and I would be very happy to hear from someone who knows it better. :)

编辑这是一篇有趣的文章关于Java中的 foreach 循环。

Edit Here is an interesting article about foreach loops in Java.

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

Another edit I analysed (with jclasslib the bytecode of these methods:

 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);
  } 
 }

两种方法都有代表通过相同的字节码:

Both methods are represented by the same bytecode:

 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

区别在于第1行,后一种方法使用 invokevirtual#8 。但是,两次调用都会导致调用 Iterator 的相同方法。因此,似乎 foreach 只不过是编译器只是转换为预定义构造的语法糖,就像文档所说的那样。这并没有回答为什么它是的问题。我觉得这很有意思,在讨论的背景下可能值得一提。

The difference is in line 1, the latter method uses invokevirtual #8. However, both invocations result in calling the same method of Iterator. So, it seems that foreach is nothing more than a syntactic sugar that the compiler just translates into a predefined construct, just as the documentation says. This does not answer the question why it is the way it is. I though this is just interesting and might be worth to know, in the context of the discussion.