今天,我的一位同事建议我重构代码,使用标签语句来控制我创建的 2 个嵌套 for 循环的流程。我以前从未使用过它们,因为我个人认为它们降低了程序的可读性。不过,如果论证足够充分,我愿意改变使用它们的想法。人们对标签声明有何看法?

有帮助吗?

解决方案

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

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

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

将标记循环分成两种方法可能很困难。例外可能太重量级了。尝试单次进入、单次退出的方法。

其他提示

标签就像 goto 的:谨慎使用它们,并且仅当它们使您的代码更快时才使用它们 更重要的是,更容易理解,

例如,如果您处于六级深度的大循环中,并且遇到一个使循环的其余部分毫无意义地完成的条件,则在条件语句中添加 6 个额外的活板门来提前退出循环是没有意义的。

标签(和 goto)并不是邪恶的,只是有时人们会以不好的方式使用它们。大多数时候,我们实际上是在尝试编写代码,以便您和下一个出现的程序员可以理解。使其变得超快是次要问题(警惕过早的优化)。

当标签(和 goto)被滥用时,它们会降低代码的可读性,这会给您和下一个开发人员带来痛苦。编译器不在乎。

在极少数情况下您需要标签,但它们可能会令人困惑,因为它们很少被使用。但是,如果您需要使用其中之一,请使用其中之一。

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

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

我很想知道你的标签替代品是什么。我认为这几乎可以归结为“尽早返回”与“尽早返回”的争论。“用一个变量来保存返回值,并且只在最后返回。”

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

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

从技术上讲,我想提前退货和唱片公司之间没有太大区别。但实际上,几乎每个 Java 开发人员都见过早期回报并知道它的作用。我猜想许多开发人员至少会对这个标签感到惊讶,并且可能会感到困惑。

我在学校里学到了单入口/单出口的正统观念,但从那以后我开始欣赏早期的 return 语句和打破循环作为简化代码并使其更清晰的一种方式。

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


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

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

  }
  purchasedItems.add(item);
}

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

例如:

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

我认为通过让标签与新的 for-each 中的变量相同,看起来非常清楚。事实上,也许 Java 应该邪恶一点,为每个变量添加隐式标签,呵呵

我使用 Java 标记循环来实现 Sieve 方法来查找素数(针对欧拉项目数学问题之一完成),这使得它比嵌套循环快 10 倍。例如 if(特定条件) 返回到外循环。

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 个嵌套循环,并且在某些条件下您想要返回到最外层循环。

所以它们有它们的用途,这取决于您想要解决的问题。

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

是的,您应该避免使用标签,除非有特定原因使用它们(简化算法实现的示例是相关的)。在这种情况下,我建议添加足够的注释或其他文档来解释其背后的原因,这样就不会有人后来出现并将其从“改进代码”或“消除代码气味”或其他一些可能是废话的借口。

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

就个人而言,标签是对我来说没有多大意义的功能之一,除了汇编语言或 BASIC 以及其他类似的有限语言之外。Java 有很多更传统/常规的循环和控制结构。

我发现标签有时在测试中很有用,可以分隔通常的设置、练习和验证阶段以及对相关语句进行分组。例如,使用 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();
}

您的格式选择可能会有所不同,但核心思想是,在本例中,标签比注释更好地在构成测试的逻辑部分之间提供了明显的区别。我觉得 斯波克 库只是建立在这个特性的基础上来声明它的测试阶段。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top