96

I'm staring at some old code from 2001 and came across this statement:

   else {
     do {
       int c = XMLDocumentFragmentScannerImpl.this.scanContent();
       if (c == 60) {
         XMLDocumentFragmentScannerImpl.this.fEntityScanner.scanChar();
         XMLDocumentFragmentScannerImpl.this.setScannerState(1);
         break label913;
       }

I had never seeen this before, and discovered labeled breaks here:

http://docs.oracle.com/javase/tutorial/java/nutsandbolts/branch.html

Doesn't this essentially function like goto? Is it even good practice to use it? It makes me uneasy.

Ondra Žižka
  • 43,948
  • 41
  • 217
  • 277
avgvstvs
  • 6,196
  • 6
  • 43
  • 74

7 Answers7

140

No, it's not like a goto, since you can't "go" to another part of the control flow.

From the page you linked:

The break statement terminates the labeled statement; it does not transfer the flow of control to the label. Control flow is transferred to the statement immediately following the labeled (terminated) statement.

This means you can only break loops that are currently being executed.

Consider this example:

first:
for( int i = 0; i < 10; i++) {
  second:
  for(int j = 0; j < 5; j ++ )
  {
    break xxx;
  }
}

third:
for( int a = 0; a < 10; a++) {

}

You could replace xxx with first or second (to break the outer or inner loop), since both loops are being executed, when you hit the break statement, but replacing xxx with third won't compile.

Motti
  • 110,860
  • 49
  • 189
  • 262
Thomas
  • 87,414
  • 12
  • 119
  • 157
  • 1
    http://docs.oracle.com/javase/tutorial/java/nutsandbolts/branch.html in case with combining continue with labels will work as goto – user_CC Feb 19 '13 at 15:23
  • 1
    @user_CC Not really, you still can't jump out of the control flow. A labeled continue will just start the next iteration of the labelled loop and will only work while that loop is already being executed. – Thomas Feb 20 '13 at 13:41
  • 3
    so If I used `break first;`, would the compiler go back up to the line immeadiately after `first:` and execute those loops again? – Ungeheuer Jun 12 '15 at 22:08
  • 19
    @JohnnyCoder no, `break first;` would _break_ (end) the loop labelled `first`, i.e. it would continue with `third`. On the other hand `continue first;` would end the current iteration of `first` and break `second` along with that and would continue with the next value of `i` in `first`. – Thomas Jun 15 '15 at 08:12
  • Well, it's a bit similar to `goto`, since this can accomplish stuff that `goto` also can in Java. – Tech Expert Wizard Nov 28 '20 at 12:52
  • 2
    @TheTechExpertGuy well, not really. It's only similar to `goto` in more or less the same sense a `if-else` statement is. Goto's are much more "flexible" as in "go anywhere", e.g. to a position before the loop (in fact that's how older languages used to implement loops: "if condition not met, go to start of loop body (again)"). I've had to deal with them recently and believe me: `goto` is a real pain in the ass while labelled breaks are not. – Thomas Dec 01 '20 at 09:56
  • @Thomas Yes, now I understand. Labelled breaks won't cause any trouble, and I can imagine how `goto` might cause trouble, although I've never used it. (I've learned C++, but I'm really just a beginner when it comes to C++, so I've never used `goto`.) Thanks! – Tech Expert Wizard Dec 01 '20 at 11:20
  • 1
    @TheTechExpertGuy you're welcome :) - Since you're a beginner, I feel inclined to give the advice once more: do not use `goto` at all, even if C++ supports it. There's almost always a better way to solve things that will save you a lot of headaches later on. The temptation might come ... resist! – Thomas Dec 01 '20 at 11:48
  • 1
    @Thomas Thanks for the advice! Well, I've also heard of stories when `goto` just causes an immense amount of trouble that it causes more trouble than the good stuff that it does. – Tech Expert Wizard Dec 01 '20 at 12:03
18

It's not as terrible as goto because it only sends control to the end of the labeled statement (typically a loop construct). The thing that makes goto unlikeable is that it is an arbitrary branch to anywhere, including labels found higher up in the method source, so that you can have home-grown looping behavior. The label-break functionality in Java doesn't allow that kind of craziness because control only goes forward.

I've only used it once about 12 years ago, in a situation where I was needing to break out of nested loops, the more-structured alternative would've made for more complicated tests in the loops. I wouldn't advise using it a lot but I wouldn't flag it as an automatic bad-code smell.

Nathan Hughes
  • 94,330
  • 19
  • 181
  • 276
  • 6
    @Boann: i don't mean to suggest goto is altogether evil in all contexts. it's just one of those things like pointer arithmetic, operator overloading, and explicit memory management that Java's creators decided was not worth the trouble. And having read COBOL programs that seemed to be over 90% gotos, I'm not sad they left it out of Java. – Nathan Hughes Feb 19 '13 at 15:29
  • 1
    Oh, you were right the first time @NathanHughes, goto is pretty damn evil. Not in all situations of course, but yea, I for one don't miss it. – Perception Feb 19 '13 at 15:44
  • 2
    goto may be more "useful" than labels, @Boann, but that's completely outweighed by their maintenance cost. labels aren't the magic wand that gotos are, and that's a good thing. – DavidS Mar 12 '16 at 01:03
12

It's always possible to replace break with new method.

Consider the code checking for any common elements in two lists:

List list1, list2;

boolean foundCommonElement = false;
for (Object el : list1) {
    if (list2.contains(el)) {
        foundCommonElement = true;
        break;
    }
}

You can rewrite it like this:

boolean haveCommonElement(List list1, List list2) {
    for (Object el : list1) {
        if (list2.contains(el)) {
            return true;
        }
    }
    return false;
}

Of course, to check for common elements between two lists it's better to use list1.retainAll(new HashSet<>(list2)) method to do that at O(n) with O(n) extra memory, or sort both lists at O(n * log n) and then find common elements at O(n).

logicray
  • 670
  • 6
  • 7
  • 1
    This is almost always the right approach in my opinion. `goto` is (potentially) harmful because it could send you so many places -- it isn't at all apparent what is being accomplished; creating a helper function gives clear bounds to the next guy as to what you're trying to do and where you're going to do it, _and_ it gives you the chance to name that piece of functionality. Way more clear this way. – ethanbustad Sep 16 '16 at 17:47
  • @ethanbustad But this is about labelled `break`, not `goto`. Sure, the refactoring shown here is a good idea in both cases. But labelled `break` is not nearly so unmoderated and prone to spaghettifying code as `goto` is. – underscore_d Jun 03 '17 at 19:56
  • This example is terrible, break doesnt work on if, the label here is just useless. – Diracnote Jul 19 '18 at 08:41
  • Wow I liked this idea. It has always been pain breaking or continuing the outer loop. Thanks. – Onur Demir Jan 29 '21 at 18:25
8

Before reading the rest of this answer, please read Go To Statement Considered Harmful. If you don't want to read it in full, here is what I consider the key point:

The unbridled use of the go to statement has an immediate consequence that it becomes terribly hard to find a meaningful set of coordinates in which to describe the process progress.

Or to rephrase, the problem with goto is that the program can arrive in the middle of a block of code, without the programmer understanding the program state at that point. The standard block-oriented constructs are designed to clearly delineate state transitions, a labeled break is intended to take the program to a specific known state (outside of the containing labeled block).

In a real-world imperative program, state is not clearly delineated by block boundaries, so it is questionable whether the labeled break is a good idea. If the block changes state that is visible from outside the block, and there are multiple points to exit the block, then a labeled break is equivalent to the primitive goto. The only difference is that, rather than the chance of landing in the middle of a block with indeterminate state, you start a new block with indeterminate state.

So, in general, I would consider a labeled break as dangerous. In my opinion it is a sign that the block should be converted into a function, with limited access to enclosing scope.

However, this example code was clearly the product of a parser generator (the OP commented that it was Xerces source code). And parser generators (or code generators in general) often take liberties with the code they generate, because they have perfect knowledge of state and humans don't need to understand it.

parsifal
  • 501
  • 2
  • 4
  • I don't agree. `break` is quite useful on several occasions. For instance, when you're looking for some value which may come from several ordered sources. You are trying one by one, and when the first is found, you `break`. It is more elegant code than `if / else if / else if / else if`. (At least it's less lines.) Moving everything to a context of a method may not always be practical. Another case is, if you are conditionally breaking from few nested levels. In short, if you don't like `break`, then you don't like `return` anywhere else except the end of a method. – Ondra Žižka Jul 24 '18 at 19:59
  • 1
    For context; "Go To Statement Considered Harmful" was written when things like `do` and `while` didn't exist and everyone was using `if(.. ) goto` everywhere instead. It was intended to encourage language designers to add support for things like `do` and `while`. Sadly, once the bandwagon started rolling it grew into a hype train fueled by ignorant people putting things like `break` into "only executed once" loops and throwing exceptions to who knows what, for all of the (rare) cases that `goto` is cleaner and less harmful. – Brendan Aug 25 '19 at 04:29
4

I don't think this is bad like the goto. And it can be useful, for example, in a scenario where you have a switch inside a for loop

FOR_STATEMENT:
for (Integer someValue : someIntegerValues) {
    SomeEnum someEnum = analyseSomeIntValueAndReturnSomeEnumResult(someValue);
    switch (someEnum) {
        SOME_ENUM_VALUE:
            //do something
            break; // <- this break will stop de switch evaluation
        SOME_OTHER_ENUM_VALUE:
            //do something
            break FOR_STATEMENT; // <- this break will stop the FOR statement
    }
}
Nando
  • 41
  • 2
2

This is not like a gotostatement where in you jump the flow control backward. A label merely shows you (the programmer) where the break has occurred. Also, the flow control is transferred to the next statement after the break.

About using it, I personally think its not of any big use as once you start writing code worth thousands of lines it becomes insignificant. But again, it would depend on the use case.

noMAD
  • 7,744
  • 19
  • 56
  • 94
1

A cleaner way of expressing the intention could be, at least in my opinion, to put the code fragment that contains the loop into a separate method and simply return from it.

For example, change this:

someLabel:
for (int j = 0; j < 5; j++)
{
    // do something
    if ...
        break someLabel;
}

into this:

private void Foo() {
    for (int j = 0; j < 5; j++)
    {
        // do something
        if ...
            return;
    }
}

This is also more idiomatic for developers proficient in other languages that might work with your code in the future (or future you).

Marek
  • 1,688
  • 1
  • 29
  • 47