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


68

今天,我有一个同事建议我重构代码,以使用label语句控制通过我创建的2个嵌套的for循环的流。我以前从未使用过它们,因为我个人认为它们会降低程序的可读性。如果论点足够扎实,我愿意改变使用它们的想法。人们对标签陈述有何看法?


8
我喜欢您的思想开放:) “我愿意改变使用它们的想法”
luigi7up 2013年

(您可以将循环放入方法中,这完全等同于方法的单次/多次/提前退出。)
greybeard

Answers:


49

如果您可以跳过两个循环(或一个包含switch语句的循环),则许多算法的表达会更容易。不要为此感到难过。另一方面,它可能表明解决方案过于复杂。因此,退后一步,看看问题所在。

有些人喜欢在所有循环中使用“单入口单出口”方法。也就是说,完全避免了循环的中断(并继续)和提早返回。这可能会导致某些重复的代码。

我强烈避免做的是引入辅助变量。将控制流隐藏在状态内会增加混乱。

将标记的循环分为两种方法可能很困难。异常可能太重了。尝试一次进入,一次退出方法。


11
除了结论“尝试一次进入,一次退出方法”外,本应对此表示赞同。
劳伦斯·多尔

1
我认为值得不时尝试一下尺寸。你总是有^ Z。
汤姆·霍汀-大头钉

2
一般而言,“隐藏状态内的控制流会增加混乱”是一个很好的建议
j_v_wow_d

34

标签就像goto的标签:谨慎使用标签,只有在它们使您的代码更快更重要,更易于理解时,

例如,如果您处于六个大级别的大循环中,并且遇到使循环的其余部分毫无意义地完成的条件,那么在条件语句中有6个额外的陷阱门就无法尽早退出循环是没有意义的。

标签(和goto的标签)不是邪恶的,只是有时人们以不良的方式使用它们。大多数时候,我们实际上是在尝试编写我们的代码,因此您和随之而来的下一个程序员都可以理解。使其超快是次要问题(请提防过早的优化)。

如果滥用标签(和goto的标签),它们会使代码的可读性降低,从而给您和下一个开发人员带来麻烦。编译器不在乎。


18
是的,+1:GOTO不会杀死应用程序-程序员会杀死应用程序。
劳伦斯·多尔

5
从历史上看,程序员使用GOTO杀死应用程序。GOTO的名字不好是有原因的。
迈克尔·复活节

一切归结于Dijkstra的信“反对Goto声明的案子” /“ Go To声明被认为有害”,这与之相反,因为倾向于将程序转换为意大利面条式代码,而不是在更结构化的内容中发现的东西。实际上,您一直都在使用goto(正在休息,继续,尝试/捕获等)。几乎总是将执行向前抛出。如果不仅仅局限于前进,则可以将功能的复杂性提高一个数量级。
BIBD

向后跳转可能很容易让您今天理解,但是三年后的下一个开发人员或您将想杀死节省了5分钟工作时间的SOB,并留下了3个小时来解析该功能的流程。
BIBD

27

在极少数情况下,您需要标签,并且由于很少使用它们,因此可能会造成混淆。但是,如果您需要使用一个,请使用一个。

顺便说一句:这编译并运行。

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

11
是的,一个好的语法荧光笔应该清楚说明原因。
劳伦斯·杜尔

24
这是一个可怕的面试问题,除非是针对其代码库完全混乱的公司。您将永远不会在野外看到它,如果您这样做,快速的网络搜索将向您展示它的含义。您可以对潜在的开发人员进行更重要的测试,而不是愚蠢的小谜语。
恢复莫妮卡2331977

1
@studro代码高亮显示和格式化将向您显示;)重点是,您将总是在别人的代码中找到东西,这与您做事的方式或期望的方式以及处理方式无关。这是您回答问题的方式,而不是给出重要问题的答案。面试应针对您所知道的知识,但您想与之打交道的方式以及如何处理各种行为的测验。
彼得·劳瑞

1
绝对是晦涩难懂的。说“这是您的处理方式”真是太好了,但是对于一个不幸的开发者来说,这是个头,幸运的是它恰巧知道了它,对于一个没有在野外遇到它的优秀开发者来说,这是一个排除因素(因为它没有根本不会发生)。“我会用Google的Java标签”在一次采访中可能没有删减。
恢复莫妮卡2331977

8

我很好奇,您可以替代标签吗?我认为这几乎可以归结为“尽早返回”与“使用变量保存返回值,并且仅在最后返回”的论点。

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


1
递归方法。或者,如果(发现)中断,则可以有条件地中断;并将其放在所有循环的末尾。您也许还可以将内部循环拆分为方法,然后返回而不是中断。您还应该质疑尽早中断的好处,是否可以通过并行化或其他算法获得更高的性能。在某些情况下,我什至意识到不需要嵌套,可以将循环简单地重构为一个接一个地运行。
Didier A.

6

我使用Java标记的循环来实现Sieve方法的方法来查找质数(针对欧拉项目中的一个数学问题完成),这使其比嵌套循环快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

我从未见过Java代码中“野外使用”的标签。如果您真的想打破嵌套循环,请查看是否可以重构您的方法,以便早期return语句可以满足您的要求。

从技术上讲,我猜早退和标签之间并没有太大区别。但是,实际上,几乎每个Java开发人员都看到了早期的回报,并且知道它做了什么。我想许多开发人员至少会对标签感到惊讶,并且可能会感到困惑。

在学校里,我曾教过单一入口/单一出口正统学,但是从那以后,我开始欣赏早期的return语句和打破循环,以此来简化代码并使之更加清晰。


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
好吧,继续nextCondition只是多余的声音。
劳伦斯·多尔

5

我认为使用新的for-each循环,标签可以非常清晰。

例如:

sentence: for(Sentence sentence: paragraph) {
  for(String word: sentence) {
    // do something
    if(isDone()) {
      continue sentence;
    }
  }
}

我认为通过在新的for-each中使标签与变量相同,可以很清楚地看出这一点。实际上,也许Java应该是邪恶的,并为每个变量添加隐式标签,呵呵


1
好了,continue sentence;不会继续处理句子(哪个?)是由内部循环处理的话:它继续与_next sentence_ /同款。
灰胡子

3

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


1

是的,除非有特殊原因使用标签,否则应避免使用标签(简化算法实现的示例与此相关)。在这种情况下,我建议添加足够的注释或其他文档来解释其背后的原因,以便以后不再有人提出来并使其脱离“改善代码”或“摆脱代码气味”的观念,或者其他潜在的BS借口。

我将这种问题等同于确定何时应该或不应该使用三元组if。最主要的理由是它可能会妨碍可读性,除非程序员非常谨慎地以合理的方式命名事物,否则使用诸如标签之类的约定可能会使事情变得更糟。假设使用“ nextCondition”和“ nextItem”的示例将“ loop1”和“ loop2”用作其标签名称。

个人标签是在Assembly或BASIC以及其他类似的受限语言之外对我而言没有太多意义的功能之一。Java具有许多更常规/常规的循环和控制构造。


1

我发现标签有时在测试中很有用,可以将通常的设置,锻炼和验证阶段以及与组相关的语句分开。例如,使用BDD术语:

@Test
public void should_Clear_Cached_Element() throws Exception {
    given: {
        elementStream = defaultStream();
        elementStream.readElement();
        Assume.assumeNotNull(elementStream.lastRead());
    }
    when:
        elementStream.clearLast();
    then:
        assertThat(elementStream.lastRead()).isEmpty();
}

您的格式选择可能会有所不同,但核心思想是在这种情况下,标签在构成测试的逻辑部分之间提供了明显的区别,胜过注释。我认为Spock库只是基于此功能来声明其测试阶段。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.