我应该避免使用Java标签语句吗?

69

今天我的同事建议我重构我的代码,使用标签语句来控制我创建的2个嵌套for循环的流程。我从未使用过它们,因为我个人认为它们会降低程序的可读性。然而,如果有足够充分的理由,我愿意改变对它们的看法。那么,人们对标签语句有什么看法呢?


8
我喜欢你思想开放的态度 :) “我愿意改变对使用它们的看法”。 - luigi7up
(由于您可以将循环放入方法中,因此这与从方法中单个/多个/提前退出完全等效。) - greybeard
12个回答

49

很多算法在跨越两个循环(或包含 switch 语句的循环)时会更容易表达。不要因此感到难过。但另一方面,这可能意味着一个过于复杂的解决方案。因此,请退后一步,看看问题本身。

有些人喜欢对所有循环采用“单入口,单出口”的方法。也就是说,完全避免使用 break(和 continue)以及提前退出循环。这可能会导致一些重复代码。

我强烈反对引入辅助变量。在状态中隐藏控制流程只会增加混乱。

将带标签的循环拆分为两个方法可能会很困难。异常可能太重了。请尝试使用单入口,单出口的方法。


11
除了结尾的“尝试使用单入口、单出口方法”的评论外,我本想给这个点赞的。 - Lawrence Dol
1
我认为值得偶尔试一试。你总是有^Z。 - Tom Hawtin - tackline
3
“将控制流程隐藏在状态中会增加混乱”是一般情况下的好建议。 - j_v_wow_d

35

标签有点像goto语句:要谨慎使用,仅当它们能使您的代码更快 并且 更重要的是,更易于理解时使用。

例如,如果您在六层深的大型循环中遇到一个条件,使得完成循环的其余部分变得无意义,那么在您的条件语句中添加6个额外的陷阱门以尽早退出循环就没有意义了。

标签(和goto语句)并不邪恶,只是有时人们会以错误的方式使用它们。大多数情况下,我们实际上是试图编写易于理解的代码,以便您和下一个程序员都能够理解。使其超级快速只是次要关注点(要谨慎避免过度优化)。

当标签(和goto语句)被误用时,它们会使代码变得不可读,这会给您和下一个开发人员带来困扰。编译器并不关心这些。


18
是的,+1:GOTO语句不会导致应用程序崩溃 - 是程序员导致应用程序崩溃。 - Lawrence Dol
5
历史上,程序员使用GOTO语句来结束应用程序。GOTO语句因此声名狼藉。 - Michael Easter
一切都归结于Dijkstra的信件《反对Goto语句的案例》/“考虑到有害的Go To语句”,该信件因其倾向于使程序成为意大利面条代码而与之争论。实际上,您一直在使用goto(包括break、continue、try/catch等)。几乎总是将执行抛出前进。如果您不仅限于向前goto,则可以将函数的复杂性增加一个数量级。 - BIBD
回到过去可能对你来说很容易理解,但是下一个开发人员或者三年后的你会想杀了那个只为节省5分钟工作时间而让他们花费3个小时解析函数流程的家伙。 - BIBD

27

有时候需要使用标签,但它们很少被使用,因此可能会让人困惑。但如果您确实需要使用,则可以使用。

顺便说一下:这可以编译并运行。

class MyFirstJavaProg {  
        public static void main(String args[]) {
           http://www.javacoffeebreak.com/java101/java101.html
           System.out.println("Hello World!");
        }
}

11
没问题,一个好的语法高亮器应该能够清楚地说明原因。 - Lawrence Dol
24
这是一个糟糕的面试问题,除非是为了一家代码极其混乱、难以理解的公司。在现实中你永远不会遇到这种情况,如果真的遇到了,只需快速搜索一下就知道它的含义。与其考察些无聊的谜语,还不如测试申请人更重要的技能。 - jr.
1
@studro的代码高亮和格式化将向您展示其内容;关键是,在别人的代码中,您总会发现不是您自己处理事情的方式或您期望的方式,那么您如何处理呢?重要的是您回答问题的方式,而不仅仅是您给出的答案。面试应该关注的是你的知识技能以及你与他人合作的方式以及如何处理各种不同的事物。 - Peter Lawrey
1
这绝对是晦涩难懂的;说“关键在于你如何处理它”虽然很好,但对于一个只是碰巧知道它的糟糕开发者来说,这是一种赠品,而对于一个还没有在实践中遇到过它的优秀开发者来说,则是一种排斥因素(因为它并不经常发生)。在面试中,“我会谷歌搜索‘Java标签’”可能行不通。 - jr.

8

我很想听听您对标签的替代方案。我认为这基本上将归结为“尽早返回”与“使用变量保存返回值,并仅在最后返回”的争论。

当嵌套循环时,标签是相当标准的。它们真正降低可读性的唯一方式是另一个开发人员从未见过它们并且不理解它们的含义。


1
递归方法。或者,你可以有条件地跳出循环:如果找到了就跳出;并且将其放在所有循环的末尾。你可能还可以将内部循环拆分成方法,并返回而不是跳出。你也应该质疑提前中断的好处,是否可以通过并行化或不同的算法获得更高的性能。在某些情况下,我甚至意识到嵌套是不必要的,循环可以简单地重构为一个接一个地运行。 - Didier A.

6

我曾经使用Java标记循环来实现素数筛法,以找到质数(为项目欧拉中的一道数学问题而完成),相比于嵌套循环,这使运行速度快了10倍。例如,如果(满足某个条件),则返回到外部循环。

private static void testByFactoring() {
    primes: for (int ctr = 0; ctr < m_toFactor.length; ctr++) {
        int toTest = m_toFactor[ctr];
        for (int ctr2 = 0; ctr2 < m_divisors.length; ctr2++) {
            // max (int) Math.sqrt(m_numberToTest) + 1 iterations
            if (toTest != m_divisors[ctr2]
                        && toTest % m_divisors[ctr2] == 0) {
                continue primes; 
            }
        } // end of the divisor loop
    } // end of primes loop
} // method

我询问了一位C++程序员关于不良标记循环的问题,他说他会谨慎使用它们,但有时候它们可能会派上用场。例如,如果您有3个嵌套循环,并且对于某些条件,您希望返回到最外层循环。

因此,它们有其用途,这取决于您试图解决的问题。


5

在某些地方,我会支持使用它们。例如,在这个例子中,我发现它们特别有用:


nextItem: for(CartItem item : user.getCart()) {

  nextCondition : for(PurchaseCondition cond : item.getConditions()) {
     if(!cond.check())
        continue nextItem;
     else
        continue nextCondition;

  }
  purchasedItems.add(item);
}

8
好的,"continue nextCondition"这一行代码是多余的且没有意义。 - Lawrence Dol

5
我认为使用新的for-each循环,标签可以变得非常清晰。
例如:
sentence: for(Sentence sentence: paragraph) {
  for(String word: sentence) {
    // do something
    if(isDone()) {
      continue sentence;
    }
  }
}

我认为通过将标签与新的for-each变量名称相同,可以使其更加清晰易懂。实际上,也许Java应该增加隐式标签用于for-each变量。


1
好的,continue sentence;并不会继续处理内部循环处理的单词所组成的句子,而是继续处理下一个句子/同一段落。 - greybeard

5
我从未在Java代码中看到过标签的使用。如果您真的想要跨越嵌套循环,请尝试重构您的方法,使早期返回语句实现您想要的功能。
从技术上讲,我想早期返回和标签之间没有太大的区别。然而,在实际操作中,几乎每个Java开发人员都见过早期返回并知道它的作用。我猜许多开发人员至少会对标签感到惊讶,并可能感到困惑。
我曾在学校里被教导单入口/单退出教义,但我后来开始欣赏早期返回语句和跳出循环作为简化代码和使其更清晰的一种方式。

3

我从不在代码中使用标签。我更喜欢创建一个守卫并将其初始化为null或其他异常值。这个守卫通常是一个结果对象。我没有看到我的同事使用标签,也没有在我们的代码库中找到任何标签。这真的取决于你编码的风格。在我看来,使用标签会降低可读性,因为它不是一个常见的结构,通常不用于Java。


1
是的,除非有特定原因需要使用label(例如简化算法实现的例子),否则应避免使用它们。在这种情况下,我建议添加足够的注释或其他文档来解释其背后的原理,以便其他人不会因为某些“改善代码”或“消除代码味道”等概念而修改它。
我认为这种问题类似于决定何时使用三元if。主要原因是它可能会影响可读性,除非程序员非常小心地合理命名,否则使用诸如标签之类的约定可能会使情况变得更糟。假设使用“loop1”和“loop2”作为标签名称的示例。
个人而言,除了汇编或BASIC等受限语言外,标签是我不太理解的功能之一。Java拥有许多更传统/常规的循环和控制结构。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接