Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Enhanced for loop compiling fine for JDK 8 but not 7

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).

like image 497
Alexander Rühl Avatar asked Oct 30 '15 14:10

Alexander Rühl


People also ask

Is enhanced for loop better?

Iterating over an array in JavaIf you only want to iterate over elements from an array or a collection e.g. a list or a set then use enhanced for loop, it's convenient and less error-prone, but if you want more control over the iteration process then use traditional for loop.

Is enhanced for loop faster Java?

For a low number of iterations (100-1000), the enhanced for loop seems to be much faster with and without JIT. On the contrary with a high number of iterations (100000000), the traditional loop is much faster.


2 Answers

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.

like image 108
Tunaki Avatar answered Oct 19 '22 19:10

Tunaki


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…

like image 11
Holger Avatar answered Oct 19 '22 18:10

Holger