Java的Continue标签已被弃用?

21

我有两个for循环,在嵌套的for循环之后,如果有一个条件为真,我不想执行一些代码。如果我使用break,那么这段代码将会被执行,因此(正如我在SCJP中学到的)我使用continue label; 来跳过外部for循环。

这是Java中已经弃用的用法吗?过时了吗?有人建议使用递归或其他方法,但对我来说,这是完全正常、简单、最新和最完美的方法。

here:
for (bla bla) {
   for (bla bla) {
      if (whatever) continue here;
   }
// some code I don't want to execute if whatever is true
}

谢谢

编辑:
如果我把我的问题改成:如何在多个嵌套的for循环之间“导航”?这种方法会被“推荐”吗?因为SCJP书中是这样说的。如果不是...这是否意味着Katherine Sierra和Bert Bates是错误的?

编辑2:
为什么不建议使用continue label;?我想要一个关于面向对象编程或Java内部工作原理的答案,可能会出现什么问题..


2
如果你需要在多个嵌套的循环之间导航,那么推荐的方法不仅是唯一的方法。所以他们是正确的。尽管我建议避免在多个嵌套的for循环之间导航。 - Mikael Vandmo
6个回答

10
答案是:这取决于具体情况。如果你发现自己经常使用continue,那么这可能意味着你的代码需要重构。然而,在你给出的场景中,似乎使用它是可以的。

8
我会重构代码使其更易读。
示例:
if (!checkWhatever()) {
    // some code I don't want to execute if whatever is true
}
boolean checkWhatever() {
    for (bla bla) {
       for (bla bla) {
          if (whatever) return false;
       }
    }
    return true;
}

5
5个嵌套的循环?听起来像是一个设计问题,需要进行重构。 - Kaj
8
没有通用的方法可以替代“继续标记”。另一方面,总有某种方法可以替代它。 - Mikael Vandmo
1
如果你有一个 Object[][][][][],你会怎么做才能避免糟糕的设计? - Cosmin Cosmin
9
@Cosmin,我会建议你不要使用 Object[][][][][][](除非你有一个五维物理问题 ;))。在某些情况下,使用类来包装 [][][] 是有意义的。 - Peter Lawrey
@Cosmin: "泛化"是设计原则,每个代码块都应该足够简单,以便立即理解。5个嵌套的循环几乎肯定不是那么简单的。解决方案是将方法的最内部部分放入一个或多个单独的方法中,使得外部控制流易于阅读:保持顶层方法简单,将细节隐藏在其他方法中。这允许读者从上到下抓住你的解决方案:大局,然后是细节。 - ToolmakerSteve
显示剩余2条评论

5

我认为这是不被鼓励的。它仍然有有效的用途,当其他替代方案更加复杂或容易出错时(即不是改进)。


即使我不认为break/continue到标签是不鼓励的,但+1。这种情况通常是嵌套循环引起的,但并非总是如此。 - Kaj
1
@Kaj。有点不同意。双重嵌套循环,如果足够简单,就不会有太多问题。需要使用标记的continue语句时,代码的可读性就会降低——这时候就需要考虑重构了——将内部循环移动到自己的方法中。Cosmin:如果代码像你的例子一样简单,那么就要遵循KISS原则。如果方法变得更加复杂,请分解内部细节,以便外部结构一目了然。并消除标记继续作为可读性的一部分。 - ToolmakerSteve

4

关于您的第二次编辑,这种做法总是会让人感到有些不妥当,因为它违反了一种比面向对象编程更久远的编程原则:结构化编程。这也有点像goto, 所有好的程序员都知道如果在他们的代码中使用了goto,他们需要去忏悔。

某些人可能会担心这样的做法会使编译器分析函数的控制流变得更加困难,但这种工具通常会被用于提高效率。例如,java.lang.String的Apache实现在以下函数中使用了它,这个函数至少旨在作为一种优化方式:

/*
 * An implementation of a String.indexOf that is supposed to perform
 * substantially better than the default algorithm if the "needle" (the
 * subString being searched for) is a constant string.
 *
 * For example, a JIT, upon encountering a call to String.indexOf(String),
 * where the needle is a constant string, may compute the values cache, md2
 * and lastChar, and change the call to the following method.
 */
@SuppressWarnings("unused")
private static int indexOf(String haystackString, String needleString,
        int cache, int md2, char lastChar) {
    char[] haystack = haystackString.value;
    int haystackOffset = haystackString.offset;
    int haystackLength = haystackString.count;
    char[] needle = needleString.value;
    int needleOffset = needleString.offset;
    int needleLength = needleString.count;
    int needleLengthMinus1 = needleLength - 1;
    int haystackEnd = haystackOffset + haystackLength;
    // Label: <----------------------------------------
    outer_loop: for (int i = haystackOffset + needleLengthMinus1; i < haystackEnd;) {
        if (lastChar == haystack[i]) {
            for (int j = 0; j < needleLengthMinus1; ++j) {
                if (needle[j + needleOffset] != haystack[i + j
                        - needleLengthMinus1]) {
                    int skip = 1;
                    if ((cache & (1 << haystack[i])) == 0) {
                        skip += j;
                    }
                    i += Math.max(md2, skip);
                    // Continue to label:  <---------------------------
                    continue outer_loop;
                }
            }
            return i - needleLengthMinus1 - haystackOffset;
        }

        if ((cache & (1 << haystack[i])) == 0) {
            i += needleLengthMinus1;
        }
        i++;
    }
    return -1;
}

1

重构使其更易读,通过将内部循环放入其自己的方法中:

for (bla bla) {   
  DoStuff();
}
void DoStuff() {
  for (bla bla) {
    if (whatever) return;
  }
  // some code to execute when whatever is false.
}

原则:如果一个方法变得足够复杂需要给一个块加标签,考虑将该方法的一部分重构为一个单独的方法,这样就不需要标签了。
同样,制作三层嵌套的方法是不明智的,除非循环非常简单。即使不需要标签也是如此。通过将复杂性隐藏在其他方法中,确保最外层的流程结构(循环、if/else或switch)易于阅读。即使这些方法只从一个地方调用。

@Mikael的想法是正确的,但是他的重构方式是不正确的——它没有保留原始代码的行为——“//一些代码”是内部循环的一部分,而不是外部循环。我建议将这个更改作为对他的帖子编辑的一部分提交,连同我的解释段落,但由于更改过大/改变了帖子的意义,被审阅者拒绝了。因此,我在这里作为一个单独的答案发布。 - ToolmakerSteve

0
使用布尔类型的变量,例如命名为“success”。 这样更容易阅读和跟踪流程。goto语句应该仅用于错误处理。
boolean success = true;
for(int outer = 0; (outer <= outerLimit) && sucess; outer++)
{
    for(int inner = 0; (inner <= innerLimit) && success; inner++)
    {
        if( !doInnerStuff() )
        {
            success = false;
        }
    }

    if( success )
    {
        success = doOuterStuff();
    }
}

你可能想在 success = false 后使用 break,或者添加一个条件以确保退出内部循环。 - njzk2

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