33

According to the Java Language Specification, § 14.14.2, the variable of an enhanced for loop must be local to the loop. In other words, this compiles:

for (State state : State.values()) {
    // do something for each state
}

but this does not:

State state;
for (state: State.values()) {
    // do something for each state
}

The JLS gives no rationale for this language design choice. I can see why the type name must be present if the local variable is modified by final or by an annotation, but I don't understand why a bare name of a variable declared elsewhere isn't allowed. Does anyone have any insight into why this restriction was imposed?

EDIT

Several answers so far seem to be suggesting that what happens outside the loop is reason for designing the language this way. Perhaps a closer examination of what the JLS says will clarify why I don't find this convincing. Consider this loop, where State is an enum:

for (State state : State.values()) {
    // ...
}

State.values() is an array, so according to the JLS, the loop is functionally identical to:

State[] a = State.values();
for (int i = 0; i < a.length; i++) {
    State state = a[i];
    // ...
}

Now clearly this latter loop could have been written:

State state;
State[] a = State.values();
for (int i = 0; i < a.length; i++) {
    state = a[i];
    // ...
}

Conceptually, this last (perfectly legal) loop could have been used as the functional equivalent of the second enhanced for loop above (the one that does not compile).

Similarly, if stateList is an Iterable<State> (not an array), this loop:

for (State state : stateList) {
    // ...
}

is functionally identical to:

for (Iterator<State> iterator = stateList.iterator(); iterator.hasNext(); ) {
    State state = iterator.next();
    // ...
}

Like before, this latter loop could have been written:

State state;
for (Iterator<State> iterator = stateList.iterator(); iterator.hasNext(); ) {
    state = iterator.next();
    // ...
}

Again, this could have been used as the functional equivalent of the (illegal):

State state;
for (state : stateList) {
    // ...
}

In each case, when the loop exits, the value of state is perfectly well defined (if, perhaps, useless). Also, just as with the regular loop, an enhanced for loop using a bare variable name that was not defined (e.g., the line State state; was missing or out of scope) could be caught at compile time. So from a language design perspective, what's the problem? Why did the language designers outlaw this construct?

Mechanical snail
  • 29,755
  • 14
  • 88
  • 113
Ted Hopp
  • 232,168
  • 48
  • 399
  • 521
  • Are they really functionally equivalent? Does an enhanced for loop have a guaranteed order? I don't know. – emory Nov 21 '11 at 00:31
  • @emory - That's what the JLS says. – Ted Hopp Nov 21 '11 at 00:35
  • Doesn't this just boil down to making definite assignment easier to figure out? If the declaration was outside, the compiler would have to verify that the loop body definitely executes at least once, in order for accesses to be allowed after the body of the loop – Damien_The_Unbeliever Nov 21 '11 at 08:09
  • @Damien_The_Unbeliever - The compiler could make the same analysis that it has to do for traditional `for` loops. I don't see this as an issue that would affect the design of the language. – Ted Hopp Nov 21 '11 at 13:28
  • @Ted - in a traditional for loop, all the compiler has to check is the `init-expression` (Or whatever it's technically called, the section between the `(` and the first `;`) for an assignment. That piece of code is *always* executed, whether any iterations *actually* occur. – Damien_The_Unbeliever Nov 21 '11 at 13:34
  • @Damien_The_Unbeliever - It's very simple for the compiler: there is no guarantee that the loop body will execute. If the variable is declared before the loop and not initialized, then it _may_ be uninitialized after the loop. Therefore the compiler will flag as an error the first code after the loop that tries to use the variable value. (The error message always uses the word "may": something like _Variable x may not have been initialized_.) – Ted Hopp Nov 21 '11 at 13:41
  • The JLS doesn't give reasons for any design choices that I can think of, and in any case that's not its purpose. Your question should really be directed to James Gosling. – user207421 Nov 21 '11 at 21:25
  • @EJP - The JLS gives rationale in a lot of places. It's not labeled "rationale" (but often it's labeled "discussion"). For instance, in §14.4.2, after stating that a `for` loop cannot declare a loop variable that shadows a local variable, the text continues, _"This restriction helps to detect some otherwise very obscure bugs. 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."_. That's rationale in my book! – Ted Hopp Nov 21 '11 at 21:39

5 Answers5

11

Look at how the for-each loop internally works, see How does the Java 'for each' loop work?

for(Iterator<String> i = someList.iterator(); i.hasNext(); ) {
String item = i.next();
System.out.println(item);
}

Each time it declares the String variable item. Hence in your case its doing essentially

State state;
\\and inside
State state = i.next();

which obviously wont work. Now inside the implementation, they it could rather only do

item = i.next();

but then you always have to define that item outside for-each, which would be pain majority of time.

Community
  • 1
  • 1
Adithya Surampudi
  • 4,354
  • 1
  • 17
  • 17
  • 8
    The language could have been designed so that the `State` declaration inside the equivalent block did not have a type name if one did not exist in the source. – Ted Hopp Nov 20 '11 at 20:28
9

One benefit/rationale is local variables dont pollute your code. Let me give a normal loop example (this is just for analogy not an exact one, so no iterator use):

int i;
for(i=0;i<10;i++)
  do...something

int j;
for(j=0; i<10; j++)
  do...something

Now in the above code if look closely you will find a potential bug. i has been mistakenly used in loop which iterates over j.

So enhanced loops try to play safe by creating the variables locally, by which you can avoid above problem.

havexz
  • 9,550
  • 2
  • 33
  • 29
  • 1
    I see your point. On the other hand, the reason I stumbled on this was that I was trying to write a loop to set a class field to each value of an enum. I ended up having to write `for (State state : State.values()) { mState = state; ... }`. This strikes me as polluting the the code with a variable (`state`) that's needed for no other purpose than to bypass the syntax restrictions on the enhanced for loop. – Ted Hopp Nov 21 '11 at 00:14
  • I agree with you that there are cases where this restriction itself pollutes code. I think thats where language designers have to choose one trade off over other. It could be chosen based on the use cases. If in general this restriction wont bother then its an acceptable solution. I agree with Bill also that Java sometimes is too formal. – havexz Nov 22 '11 at 20:23
7

A lot of decisions in Java are more based on the concept of why "Wouldn't" you remove x. Why on earth allow your code to be confused by moving the scope outside the loop? If you really needed access to the last element afterwards there are cleaner ways.

I suppose some may argue one way or another as to protecting programmers from themselves, I look at it more as Java protects me from people who like to take advantage of things like this.

It's not really a great hobby language, it is a great team development language where the point is to make it as easy as possible for the next guy to understand your code.

For instance, I saw in a comment above that many people break out of a for loop at a certain point with a value. How clear is that when you are scanning through code trying to find a bug? It's people using neat tricks like this that I really want to be protected from, not myself.

If you wish to execute some code mid-loop, put it in a method and call it from within the loop.

The lack of cute tricks often makes me re-think my code and work a little harder--but the results are always more readable/maintainable.

If you are just a single programmer (Not on a team) I'd advise against Java, It doesn't do neat tricks and you won't see cool yearly feature updates--and it is fairly slow to program in (It moves a lot of time catching and fixing bugs into time spent coding, frustrating people with it's strict rules (One of the biggest advantages of the language IMO)

If you want something javaish try Scala instead. If you are "Forced" to learn it because of a classroom decision then you might want to attempt to appreciate it from the "team" point of view and with the mindset that you won't be forced to use it in the future.

Bill K
  • 62,186
  • 18
  • 105
  • 157
  • Wouldn't this same line of reasoning apply to the regular `for` loop? There are cases where forcing the scope to be strictly within the loop creates confusion (e.g., additional variables or otherwise unnecessary function calls). – Ted Hopp Nov 21 '11 at 05:50
  • It could be argued I suppose--in fact I might not object to removing the other syntax entirely (along with public variables, checked exceptions and a few other parts). Forcing you to re-think many loops into iterators might really help a lot of code, but would drive many people nuts. – Bill K Nov 21 '11 at 05:59
  • Interesting. If this was the thinking of the designers of the enhanced `for` loop, it's too bad they didn't explain themselves in the JLS. I'm just wondering if there's a technical (as opposed to philosophical) reason for the restriction. (Perhaps, for instance, it allows for certain compiler optimizations.) – Ted Hopp Nov 22 '11 at 22:39
5

They might have wanted to remove a possible difference in semantics between the simple for and the enhanced for.

If you had, say, a regular for loop:

int i;
for (i=0; i<4; i++)
    ;

then if you let the loop execute normally, you get that i==4 after the loop, which is not valid for the iteration variable.

What then, if you could have:

int i;
for (i : myArray)
    ;

I suppose from an implementation standpoint, it would be easiest if at the end i were equal to the last element in the array. But should it behave like that or should there be some invalid value, and if so, what could that be? What if you break out of the last iteration?

Another possibility is that it makes it more clear what that loop is about and what the element type of the collection is. By contrast, the simple for is a "legacy" construct in some sense, so they couldn't have applied a similar logic without "breaking" it or limiting its flexibility.

I'm obviously speculating, and it could turn out to have just been a random design choice.

Vlad
  • 18,195
  • 4
  • 41
  • 71
  • 1
    There are many scenarios where the value of the loop variable after the loop exits is useful. A common example is iterating through a Collection and breaking at the first element that satisfied some predicate. Since the loop variable of an enhanced for loop goes out of scope, it unfortunately can't be used to for this. (One has to have two variables -- the loop variable and another variable into which to stash the loop variable value before breaking.) – Ted Hopp Nov 21 '11 at 01:31
  • I agree about the usefulness, and I was thinking about the same use case. But I'm not sure, in the case of the enhanced loop, if you'd be able to distinguish between a break in the last iteration, and no break. In both cases, the value of the iteration variable would be the last value in the collection. You could argue `null` but then you can also have `nulls` in the collection. So, you might need an extra variable to check anyway. – Vlad Nov 21 '11 at 06:04
3

Maybe because in this way, it guarantees that state will be empty and has no initial value assigned to it.

Eng.Fouad
  • 115,165
  • 71
  • 313
  • 417