Enhanced for loop compiling fine for JDK 8 but not 7

懵懂的女人 提交于 2019-11-30 16:36:58

问题


Consider the following code snippet, I stumpled upon after some refactoring, when checkin why the build server reported a broken build but it was fine in my IDE:

List<String> text;
...
for (String text : text) {...}

So, the same name is used for the String and the List within the for-each.

This is of course not very wise to do, but after following my nosiness before renaming it, I saw that the above code compiles fine with JDK 8, but gives the below error with JDK 7:

  error: for-each not applicable to expression type
        for (String text : text) {
                           ^
  required: array or java.lang.Iterable
  found:    String
1 error

I know that changes were made to several parts in this area within the JDK - but can someone enlighten me on why exactly this behaviour occurs?


Update: Since I got some comments about different behaviour, here's a full sample class:

import java.util.Arrays;
import java.util.List;

public class Strange {

    List<String> text = Arrays.asList("Max", "Alex", "Maria");

    public static void main(String[] args) {
        new Strange().doSomething("Alex");
    }

    public void doSomething(String name) {
        for (String text : text) {
            System.out.println(text.equals("Alex"));
        }
    }

}

And here's the compile process and output (Windows 7 64bit):

C:\copy>c:\Projects\java\jdk1.7.0_79\bin\javac.exe Strange.java
Strange.java:13: error: for-each not applicable to expression type
        for (String text : text) {
                           ^
  required: array or java.lang.Iterable
  found:    String
1 error

C:\copy>c:\Projects\java\jdk1.8.0_60\bin\javac.exe Strange.java

C:\copy>

Conclusion: I was so puzzled why my IDE (which uses 8) didn't complain about twice the same name in one statement - but now it is clear that it is not one statement. I really wonder why this point has so long been in place if the JLS states otherwise. But anyway, thanks for the insights I have received and the great answers (which made it hard for me to pick the best one).


回答1:


While the reasoning, using the specified translation from the enhanced for loop to the traditional for loop, used by other answers is correct, there is an explicit specification about the scopes:

§6.3. Scope of a Declaration

The scope of a local variable declared in the FormalParameter part of an enhanced for statement (§14.14.2) is the contained Statement.

(direct link)

Thus, the scope of the variable does not include the Expression of the enhanced for loop…

You can verify that this hasn’t changed, compared to Java 7 and Java 6, though both (I tried Java 6 javac) exhibit the contradicting behavior.

So this change in the compiler behavior is the fix of an old bug…




回答2:


This should actually compile fine for JDK 7 and 8.

Quoting JLS section 14.14.2 (which is the same for the Java 7 specification):

The enhanced for statement is equivalent to a basic for statement of the form:

for (I #i = Expression.iterator(); #i.hasNext(); ) {
      {VariableModifier} TargetType Identifier =
          (TargetType) #i.next();
      Statement
}

Rewriting the enhanched for loop with Iterator

for (String text : text) {...}

becomes

for (Iterator<String> it = text.iterator(); it.hasNext(); ) {
    String text = it.next();
}

Then, quoting example 6.4.1 of the JLS:

A similar restriction on shadowing of members by local variables was judged impractical, because the addition of a member in a superclass could cause subclasses to have to rename local variables. Related considerations make restrictions on shadowing of local variables by members of nested classes, or on shadowing of local variables by local variables declared within nested classes unattractive as well.

As such, there is no compile-time error here because no restriction is made when shadowing a member variable by a local variable, which is the case here: the local variable String text is shadowing the member variable List<String> text.




回答3:


I would say it is a compiler bug in the particular version of the Java 7 compiler that you are using.

The earlier text is a field, and it is legal for the text local declared in the for statement to shadow a field.

Then we look at what the for loop means. According to the JLS,

    for (String text : text) {...}

is equivalent to

    for (Iterator<String> #i = text.iterator(); #i.hasNext(); ) {
        String text = (String) #i.next();
        ...
    }

As you can see the inner text is not in-scope for the text.iterator() expression.


I tried searching the Oracle Java Bugs Database, but couldn't find anything that matched this scenario.




回答4:


Although I think the other answers are correct, let me be devil's advocate and offer the opposite view.

Obviously JDK 7 parses the foreach loop in such a way that the variable 'text' is also in scope after the ':'. To test this, I wrote the following method. It compiles and runs just fine in Java 1.7:

public static void main(String[] args) {
    for (String text : new String[] {text = "hello", text, text, text})
        System.out.println(text);
}

Although others have said this is a bug in jdk 1.7 (and it probably is), I couldn't find anywhere in the JLS that specifically says the variable just declared is not in scope after the ':'. If it's not a bug, then Java 8 breaks compatibility.




回答5:


Your build server may be compiling using a different jdk than your local machine. (Not just a different version number, but a completely different implementation.) Eclipse is one that uses its own compiler, I believe to facilitate its code hot-swapping.

Using the same name for the collection and the element ought to raise problems anywhere, but I have heard of and occasionally noticed Eclipse tolerating things that the Sun/Oracle JDK won't.




回答6:


This compiled fine for me. I'm using the Java 8 JDK, on Netbeans, on a 64 bit machine (Windows 7).

I believe this is a localization issue related to your IDE or compiler. I used your exact example, the output being

false
true
false

There was a warning given, stating that it is possible, but not recommended to hide a field with a local variable.



来源:https://stackoverflow.com/questions/33438842/enhanced-for-loop-compiling-fine-for-jdk-8-but-not-7

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!