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

Ted*_*opp 32 java foreach language-design

根据Java语言规范,§14.14.2,增强for循环的变量必须是循环的局部变量.换句话说,这编译:

for (State state : State.values()) {
    // do something for each state
}
Run Code Online (Sandbox Code Playgroud)

但这不是:

State state;
for (state: State.values()) {
    // do something for each state
}
Run Code Online (Sandbox Code Playgroud)

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

编辑

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

for (State state : State.values()) {
    // ...
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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

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

for (State state : stateList) {
    // ...
}
Run Code Online (Sandbox Code Playgroud)

功能相同:

for (Iterator<State> iterator = stateList.iterator(); iterator.hasNext(); ) {
    State state = iterator.next();
    // ...
}
Run Code Online (Sandbox Code Playgroud)

像以前一样,后一个循环可能已写入:

State state;
for (Iterator<State> iterator = stateList.iterator(); iterator.hasNext(); ) {
    state = iterator.next();
    // ...
}
Run Code Online (Sandbox Code Playgroud)

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

State state;
for (state : stateList) {
    // ...
}
Run Code Online (Sandbox Code Playgroud)

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

小智 11

看看for-each循环内部如何工作,请参阅每个循环的Java'如何工作?

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

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

State state;
\\and inside
State state = i.next();
Run Code Online (Sandbox Code Playgroud)

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

item = i.next();
Run Code Online (Sandbox Code Playgroud)

但是你总是必须在每个外面定义那个项目,这大部分时间都是痛苦的.

  • 可以设计该语言,以便等效块内的"State"声明没有类型名称(如果源中不存在). (8认同)

hav*_*exz 9

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

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

int j;
for(j=0; i<10; j++)
  do...something
Run Code Online (Sandbox Code Playgroud)

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

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


Bil*_*l K 7

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

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

它不是一种非常好的业余语言,它是一种出色的团队开发语言,其重点是让下一个人能够轻松理解您的代码.

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

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

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

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

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


Vla*_*lad 5

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

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

int i;
for (i=0; i<4; i++)
    ;
Run Code Online (Sandbox Code Playgroud)

那么如果让循环正常执行,那么i==4在循环之后就会得到它,这对于迭代变量是无效的.

然后,如果你可以:

int i;
for (i : myArray)
    ;
Run Code Online (Sandbox Code Playgroud)

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

另一种可能性是它更清楚地表明循环是什么以及集合的元素类型是什么.相比之下,简单的是某种意义上的"遗留"构造,因此它们不能在不"破坏"它或限制其灵活性的情况下应用类似的逻辑.

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