I know that the foreach loop used开发者_如何学JAVA in the following example doesn't compile. But does anybody know why using a field in a foreach loop declaration isn't allowed?
public class Foo {
private Object obj;
public void run(List<Object> objects) {
for (obj : objects) {
process();
}
}
private void process() {
// do something with obj
}
}
Probably because it
- limits the scope of the loop variable (thus making the code clearer, and avoiding possible subtle bugs), and
- simplifies parsing for the compiler.
It generally doesn't make sense to assign to a field in a foreach loop. The scope of an object in a foreach loop is just the one iteration of the loop, whereas the scope of a field is the lifetime of the object.
You should really just pass each object as an argument to process()
... you wouldn't be gaining anything by storing the reference as a field. Plus, if you really really want to you can just manually assign to a field using this.obj = obj
.
I expect there are a few reasons, but it probably just comes down to preventing programmer error.
One thing that would be confusing is "what would be the value of obj after the loop had executed"? Unlike the standard for-loop, the enhanced for-each loop isn't trying to make guarantees about its own mechanics.
Another thing is that instance fields represent an object's state. What you'd be saying by using an instance field in a for-each loop is that the object could changes from one state, then to one or many intermediate states, then to final a state during the course of a single operation. That's just bad design, and worth preventing.
Why not pass obj as an argument to process()
?
What value should obj
have before the for loop is run, and what value should it have after the loop is run? This would be confusing.
There was a bug report on this issue back in 2004. It was rejected and the response is interesting:
There's a certain 'safety-first' approach taken by forcing the loop variable to be declared inside the loop (see translation in JLS 14.14.2). First, there's no danger of clobbering a variable in the enclosing scope accidentally. Second, using a fresh loop variable every iteration avoids concurrency problems if the variable is handed to other threads by the loop body. Third, the compiler may optimize by declaring the loop variable as 'final'. Fourth, if a variable in the enclosing scope was reused as the loop variable, and a programmer had the idea that the loop walks the entire collection, they might expect to be able to observe the last element in the collection after iteration, via the loop variable. But it wouldn't necessarily refer to the last element, e.g. if the loop did a break.
精彩评论