为什么 Java 允许在任意语句上使用带标签的中断?
我今天刚刚了解到以下 Java 代码是完全合法的:
myBlock: {
/* ... code ... */
if (doneExecutingThisBlock())
break myBlock;
/* ... more code ... */
}
请注意,myBlock
不是循环 - 它只是我用大括号分隔的代码块。
这似乎是一个相当奇怪的功能。这意味着您可以使用命名的 break
来中断 if
语句或匿名块,尽管您通常不能使用 break
在这些上下文中的声明。
我的问题是:这个设计决策有充分的理由吗?也就是说,为什么要这样做,以便只能使用带标签的 break
打破某些封闭语句但不是常规的break
?为什么要允许这种行为呢?鉴于 Java 作为一种语言(相对而言)设计良好,我认为这是有原因的,但老实说我想不出一个原因。
I just learned today that the following Java code is perfectly legal:
myBlock: {
/* ... code ... */
if (doneExecutingThisBlock())
break myBlock;
/* ... more code ... */
}
Note that myBlock
isn't a loop - it's just a block of code I've delimited with curly braces.
This seems like a rather strange feature to have. It means that you can use a named break
to break out of an if
statement or anonymous block, though you can't normally use a break
statement in these contexts.
My question is this: is there a good reason for this design decision? That is, why make it so that you can only break out of certain enclosing statements using labeled break
s but not regular break
s? And why allow for this behavior at all? Given how (comparatively) well-designed Java is as a language I would assume there's a reason for this, but I honestly can't think of one.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
这样做似乎是为了简单起见。如果最初标记的break只能中断循环语句,那么语言设计者应该立即清楚该限制是不必要的,语义对于所有语句都是相同的。为了语言规范的经济性和编译器的更简单实现,或者只是出于通用性的习惯,为任何语句定义标记为break,而不仅仅是循环语句。
现在我们可以回顾并判断这个选择。通过赋予程序员额外的表达能力,这对程序员有好处吗?看起来很少,这个功能很少用到。程序员需要学习和理解吗?看来是这样,正如本次讨论所证明的那样。
如果你能回到过去并改变它,你愿意吗?我不能说我会。我们有一种对普遍性的迷恋。
如果在平行宇宙中它仅限于循环语句,那么仍然有机会(可能要小得多)有人在 stackoverflow 上发布问题:为什么它不能处理任意语句?
It is plausible that this was done for simplicity. If originally the labeled break can only break loop statements, then it should be immediately clear to language designer that the restriction isn't necessary, the semantics work the same for all statements. For the economics of the language spec, and simpler implementation of compilers, or just out of the habit towards generality, labeled break is defined for any statement, not just loop statements.
Now we can look back and judge this choice. Does it benefit programmers, by giving them extra expression power? Seems very little, the feature is rarely used. Does it cost programmers in learning and understanding? Seems so, as evidenced by this discussion.
If you could go back time and change it, would you? I can't say I would. We have a fetish for generality.
If in a parallel universe it was limited to loop statements only, there is still a chance, probably much smaller, that someone posts the question on stackoverflow: why couldn't it work on arbitrary statements?
将其视为从块返回而不是从整个函数返回的
return
语句。适用于对象到break
分散在任何地方的相同推理也可以适用于允许return
被允许在除函数末尾之外的任何地方。Think of it as a
return
statement that returns from the block instead of from the entire function. The same reasoning you apply to object tobreak
being scattered anywhere can also be applied toreturn
being allowed anywhere except at the end of a function.goto 的问题是它可以向前跳转,跳过代码。带标签的中断不能做到这一点(它只能向后)。 IIRC C++ 必须处理 goto 跳转过去的代码(自从我关心这个问题以来已经有 17 年多了,所以我不确定我是否记得正确)。
Java 被设计为供 C/C++ 程序员使用,因此做了很多事情来让这些开发人员熟悉它。可以进行从 C/C++ 到 Java 的合理转换(尽管有些事情并非微不足道)。
可以合理地认为,他们将其放入语言中是为了给 C/C++ 开发人员一个安全的跳转(您只能在代码中向后跳转),从而使某些程序员能够更轻松地进行转换。
我从未见过它被使用过,而且在 16 年多的 Java 编程生涯中我也很少见过带标签的中断。
你不能向前突破:
The issue with goto is that it can jump forward, past code. A labeled break cannot do that (it can only go backwards). IIRC C++ has to deal with goto jumping past code (it is been over 17 years since I cared about that though so I am not sure I am remembering that right).
Java was designed to be used by C/C++ programmers, so many things were done to make it familiar to those developers. It is possible to do a reasonable translation from C/C++ to Java (though some things are not trivial).
It is reasonable to think that they put that into the language to give C/C++ developers a safe goto (where you can only go backwards in the code) to make it more comfortable to some programmers converting over.
I have never seen that in use, and I have rarely seen a labeled break at all in 16+ years of Java programming.
You cannot break forward:
考虑:
如果
break
按照您的建议执行,则此代码将意外执行。休息会变得更加难以使用。我不使用它,但它是一个功能,并且允许某些独特的控制流构造。我想问你,为什么不允许允许呢?
Consider:
If the
break
did as you suggest, this code would perform unexpectedly. Breaks would become a lot more difficult to use.I don't use it, but it is a feature and allows for certain unique control-flow constructs. I'd ask you, why not allow it?
是的。因为它有效。
在标记为“break”的情况下,您不需要处于循环或 switch 内,这一事实使您可以表达用其他方式难以表达的内容。 (诚然,人们很少以这种方式使用标记的中断......但这不是语言设计的错误。)
在未标记的中断情况下,行为是打破最内层的封闭循环或开关。如果要突破最内层的封闭语句,那么很多事情将更难以表达,并且许多事情可能需要一个标记块。例如:
如果
break
脱离了最内层的封闭语句,那么它就不会脱离循环。还有另一个可能的原因/基本原理。请记住,Java 是一种全新的语言,也是异常和异常处理的较早采用者。
考虑一下:
根据教条,这是糟糕的代码。您不应该使用异常来进行“正常”流量控制。
(实际上,由于异常创建和处理的开销,它的效率也非常低。我认为,Java 8 中的异常性能得到了显着提高,但那是大约 20 年后的事了。)
现在想象一下,您是一名语言设计师,并且您觉得你必须提供“异常作为流量控制”反模式的替代方案。 “
break
to label”结构正是这样做的。将上面的内容与问题中的示例进行比较。事后看来,这是没有必要的。上述可以通过其他方式完成;即没有标记的中断。实际上,这种构造很少被使用,以至于许多(也许是大多数)程序员甚至不知道 Java 中存在它。
Yes. Because it works.
In the labelled break case, the fact that you don't need to be inside a loop or switch lets you to express things that are harder to express in other ways. (Admittedly, people rarely do use labelled break this way ... but that's not a fault of the language design.)
In the unlabelled break case, the behavior is to break out of the innermost enclosing loop or switch. If it was to break out of the innermost enclosing statement, then a lot of things would be much harder to express, and many would probably require a labelled block. For example:
If
break
broke out of the innermost enclosing statement, then it wouldn't break out of the loop.There is another possible reason / rationale. Remember that Java was a brand new language and a relatively early adopter of exceptions and exception handling.
Consider this:
According to the dogma, that is bad code. You shouldn't use exceptions for "normal" flow control.
(Pragmatically, that it also very inefficient due to the overheads of exception creation and handling. Exceptions performance was improved significantly in Java 8, I think, but that was ~20 years later.)
Now imagine that you are a language designer, and you feel that you have to provide an alternative to the "exceptions as flow control" anti-pattern. The "
break
to label" construct does exactly that. Compare the above with the example in the question.In hindsight, this is unnecessary. The above can be done in other ways; i.e. without labelled break. In practice this construct is used so rarely that many (maybe most) programmers don't even know it exists in Java.
在 Java 之前,多种编程语言已经实现了留下语句序列的功能。两个例子:
Algol-68 有 exit 来终止最小封闭子句的执行(非常宽松地说,开始 ... 结束顺序)。
BLISS 标记了 BEGIN … END 块,并用 LEAVE 语句终止执行。
带标签的实现(如 Java 中)更加灵活,因为它们可以退出嵌套块(或复合语句,或您在选择的语言中对它们的称呼);如果没有标签,您只能退出一个“级别”。
回答直接的问题“为什么”——因为它被发现在其他先前的语言中是有用的结构。
The ability to leave a sequence of statements has been implemented in several programming languages before Java. Two examples:
Algol-68 had exit to terminate the execution of the smallest closed-clause (very loosely speaking, a begin ... end sequence).
BLISS had labelled BEGIN … END blocks, with a LEAVE statement to terminate execution.
Implementations with labels (as in Java) are more flexible in that they can exit nested blocks (or compound statements, or whatever you call them in your language of choice); without the label, you're limited to exiting a single "level" only.
Answering the direct question, "why" -- because it's been found to be a useful construct in other, prior, languages.
添加到 Stephen C 的答案中,
if (something)
你无法打破嵌套循环。这些情况在数值算法中确实会发生。这里有一个简单的例子 - 如果没有指定的 for,你就无法跳出 i 循环。希望这有帮助。Adding to Stephen C's answer,
if (something)
you cannot break out of a nested loop. These situations do happen in numerical algorithms. One simple example here - you cannot break out of the i-loop without the named for. Hope this helps.它是相当于 goto 的“结构化”,在某些情况下很有用。
我经常使用这样的标签在方法中创建命名子块,以严格限制变量的范围或简单地标记不适合分解为单独函数的代码块。也就是说,我用它来标记一个块,以便保留大括号周围的代码结构。下面是 C 语言中 JNI 调用的示例,我在 Java 中也做了同样的事情:
It's the "structured" equivalent to a goto, useful in certain circumstances.
I quite often use such a label create named sub-blocks in a method to tightly limit scope of variables or to simply label a block of code which is not appropriate to break out into a separate function. That is, I use it to label a block so that the code structure around braces is preserved. Here's an example in C for a JNI call, and I do the same in Java:
退出嵌套循环似乎很有用。请参阅 http://download.oracle.com/javase/tutorial/java /nutsandbolts/branch.html
它在语义上与 C# 中是否有相当于 Java 标记中断的功能或解决方法
It seems to be useful to exit nested loops. See http://download.oracle.com/javase/tutorial/java/nutsandbolts/branch.html
It's semantically the same as is there a equivalent of Java's labelled break in C# or a workaround