为什么增强的for循环的局部变量必须是本地的?

时间:2011-11-20 20:09:22

标签: java foreach language-design

根据Java Language Specification, § 14.14.2,增强for循环的变量必须是循环的局部变量。换句话说,这编译:

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

但这不是:

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

JLS没有给出这种语言设计选择的理由。我可以看到为什么如果局部变量被final或注释修改,则必须存在类型名称,但我不明白为什么不允许在其他地方声明的变量的裸名称。有没有人知道为什么要施加这种限制?

修改

到目前为止,有几个答案似乎暗示在循环之外发生的事情是以这种方式设计语言的原因。或许仔细研究JLS所说的将澄清为什么我没有发现这令人信服。考虑这个循环,其中State是枚举:

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

State.values()是一个数组,因此根据JLS,循环在功能上与:

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

现在显然可以写出后一个循环:

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

从概念上讲,这最后一个(完全合法的)循环可以用作上面第二个增强for循环的功能等价物(不编译的循环)。

同样,如果stateListIterable<State>(不是数组),则此循环:

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

在功能上与:

相同
for (Iterator<State> iterator = stateList.iterator(); iterator.hasNext(); ) {
    State state = iterator.next();
    // ...
}
像以前一样,后一个循环 本来可以写的:

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

同样,这个可以被用作(非法)的功能等价物:

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

在每种情况下,当循环退出时,state的值被很好地定义(如果,也许,无用)。此外,与常规循环一样,可以在编译时捕获使用未定义的裸变量名称的增强for循环(例如,行State state;缺失或超出范围)。那么从语言设计的角度来看,问题是什么?为什么语言设计者禁止这种结构呢?

5 个答案:

答案 0 :(得分:11)

查看for-each循环内部如何工作,请参阅How does the Java 'for each' loop work?

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

每次声明String变量项。因此,在你的情况下,它基本上是

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

显然不会工作。现在在实现中,他们只能做

item = i.next();

但是你总是必须在for-each之外定义该项目,这在很大程度上是痛苦的。

答案 1 :(得分:9)

一个好处/理由是局部变量不会污染您的代码。让我给出一个正常的循环示例(这只是为了类比而不是一个精确的,所以没有使用迭代器):

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

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

现在在上面的代码中如果仔细观察,你会发现一个潜在的错误。 i被错误地用在循环中,迭代j

因此,增强的循环尝试通过在本地创建变量来保证安全,通过它可以避免上述问题。

答案 2 :(得分:7)

Java中的许多决策更多地基于为什么“不会”删除x的概念。为什么通过将范围移到循环外来允许您的代码混淆?如果你真的需要访问最后一个元素,那么有更简洁的方法。

我想有些人可能会以这样或那样的方式争论保护程序员自己,我更多地看待它,因为Java保护我免受那些喜欢利用这类事情的人的保护。

这不是一个非常好的爱好语言,它是一种很棒的团队开发语言,其重点是让下一个人能够轻松理解你的代码。

例如,我在上面的评论中看到,许多人在某个特定点突破了一个for循环。当您扫描代码试图找到错误时,有多清楚?这是人们使用这样的巧妙技巧,我真的想要保护,而不是我自己。

如果您希望在循环中执行某些代码,请将其放在方法中并在循环内调用它。

缺乏可爱的技巧常常让我重新思考我的代码并且工作稍微努力 - 但结果总是更易读/可维护。

如果你只是一个程序员(不在团队中),我会建议不要使用Java,它不会做很好的技巧,你也不会看到很酷的年度功能更新 - 而且编程速度相当慢(它将很多时间用于捕获并修复错误到编码所花费的时间,使用它的严格规则令人沮丧(IMO语言的最大优势之一)

如果你想要一些javaish尝试Scala。如果你因为课堂决定而被“强迫”学习它,那么你可能想要尝试从“团队”的角度来理解它,以及你将来不会被迫使用它的心态。 / p>

答案 3 :(得分:5)

他们可能想要删除simple for和。之间可能存在的语义差异。

如果你有一个常规的for循环:

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

然后如果你让循环正常执行,你会在循环之后得到i==4,这对迭代变量无效。

然后,如果你可以:

int i;
for (i : myArray)
    ;

我认为从实现的角度来看,如果最后i等于数组中的最后一个元素,那将是最简单的。但是它应该表现得那样还是应该有一些无效的价值,如果是这样的话,会是什么呢?如果你突破上一次迭代怎么办?

另一种可能性是它更清楚地说明了循环是什么以及集合的元素类型是什么。相比之下,简单的就是&#34;遗产&#34;从某种意义上讲,他们无法在没有“打破”的情况下应用类似的逻辑。它或限制其灵活性。

我显然在猜测,结果可能只是一个随机的设计选择。

答案 4 :(得分:3)

可能因为这样,它保证state为空并且没有分配初始值。