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

发布于 2024-12-16 20:04:21 字数 2011 浏览 1 评论 0原文

根据 Java 语言规范,§ 14.14.2< /a>,增强型 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 (不是数组),则此循环:

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; 丢失或超出范围) ) 可以在编译时捕获。那么从语言设计的角度来看,问题出在哪里呢?为什么语言设计者禁止这种构造?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

清醇 2024-12-23 20:04:21

查看 for-each 循环内部是如何工作的,请参阅 Java 是如何工作的'for every' 循环有效吗?

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

每次它都声明 String 变量 item。因此,在你的情况下,它本质上

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

显然是行不通的。现在在实现中,它们只能做

item = i.next();

,但你总是必须在 for-each 之外定义该项目,这在大多数情况下会很痛苦。

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.

廻憶裏菂餘溫 2024-12-23 20:04:21

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

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

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

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

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

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.

我乃一代侩神 2024-12-23 20:04:21

Java 中的许多决策更多地基于“为什么”不”删除 x 的概念。为什么通过将作用域移到循环之外而让您的代码变得混乱?如果您确实需要随后访问最后一个元素,则有更简洁的方法。

我想有些人可能会以某种方式争论如何保护程序员免受自己的伤害,我更多地认为 Java 保护我免受那些喜欢利用此类事情的人的伤害。

它并不是一种真正的业余爱好语言,而是一种很棒的团队开发语言,其重点是让下一个人尽可能轻松地理解您的代码。

例如,我在上面的评论中看到,许多人在某个时刻用一个值打破了 for 循环。当您扫描代码试图找到错误时,这一点有多清晰?我真正想要保护的是那些使用这种巧妙伎俩的人,而不是我自己。

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

缺乏可爱的技巧常常让我重新思考我的代码并更加努力地工作——但结果总是更具可读性/可维护性。

如果你只是一个程序员(不在一个团队中),我建议不要使用 Java,它没有巧妙的技巧,你不会看到很酷的年度功能更新——而且它的编程速度相当慢(它移动大量的时间花在编码上来捕捉和修复错误,它的严格规则让人们感到沮丧(IMO 语言的最大优势之一)

如果你想要一些 Java 的东西,如果你因为“被迫”学习它而尝试 Scala。然后进行课堂决定您可能想尝试从“团队”的角度来欣赏它,并以您将来不会被迫使用它的心态来欣赏它。

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.

老子叫无熙 2024-12-23 20:04:21

他们可能希望消除简单 for 和增强 for 之间可能存在的语义差异。

例如,如果您有一个常规的 for 循环:

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

那么如果您让循环正常执行,您会在循环后得到 i==4,这对于迭代变量无效。

那么,如果您可以:

int i;
for (i : myArray)
    ;

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

另一种可能性是,它使循环的内容以及集合的元素类型是什么更加清楚。相比之下,简单的 for 在某种意义上是“遗留”构造,因此他们无法在不“破坏”它或限制其灵活性的情况下应用类似的逻辑。

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

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.

请你别敷衍 2024-12-23 20:04:21

也许是因为这样,它保证 state 将为空,并且没有为其分配初始值。

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文