4 things you didn't know about the labeled statement in Java

Marcio Endo
April 16, 2023

Welcome to Objectos Weekly issue #022.

Earlier this week, I saw a tweet from the Red Hat Java team. It shows a variation of "Puzzle 22: Dupe of URL" from the Java Puzzlers book by Joshua Bloch and Neal Gafter. The book was originally published in 2005. So, depending on how long you've been programming in Java, chances are the code did not puzzle you; it is a label from a labeled statement immediately followed by an end-of-line comment.

Though this might have been one leaning towards the trivial side, I must say I am not a fan of code puzzlers. It is ironic since I do own a copy of the book (whose name is "Java Puzzlers"). I think the main reason is that I am very bad at solving them.

On the other hand, I do like to learn and write about the little known bits of the language.

So, in this issue, I will show 4 things you maybe didn't know about the labeled statement in Java.

Let's begin.

1. You can label almost all statements

The first thing to know about labeled statements is that you can put a label on almost any Java statement. There is one exception (or two) which we will see in a bit.

This means that labels are not restricted to e.g. for or while statements. Though, to be fair, labels have no practical use in most of the other statements.

Consider the following Java program:

import static java.lang.System.out;

public class LabeledStatement {
  public static void main(String[] args) {
    question: out.println("Does this compile?");

    branch: if (args.length > 0) {
      out.println("It has args!");
    } else {
      out.println("No args...");
    }

    assertion: assert args != null : "uh-oh";

    empty: ;

    block: {
      break block;
    }

    now: break now;

    sync: synchronized (LabeledStatement.class) {
      out.println("Synchronized statement");
      break sync;
    }

    end: out.println(new LabeledStatement());
  }

  @Override
  public String toString() {
    toString: return "Labeled statements!";
  }
}

Notice how each statement in the program has a label. Even the empty statement has a label.

It compiles without errors. When using javac it also compiles without warnings. When using the Eclipse JDT compiler you need to suppress the unused compiler warnings.

It also executes normally. When executed it prints:

Does this compile?
No args...
Synchronized statement
Labeled statements!

This is all defined in the Java Language Specification (JLS).

It is all in the JLS

The labeled statement is defined in Section 14.7 of the JLS:

LabeledStatement:
  Identifier : Statement
  
LabeledStatementNoShortIf:
  Identifier : StatementNoShortIf

So any Statement can be labeled.

But what exactly is under the Statement production? It is defined in Section 14.5 of the JLS:

Statement:
  StatementWithoutTrailingSubstatement
  LabeledStatement
  IfThenStatement
  IfThenElseStatement
  WhileStatement
  ForStatement

StatementNoShortIf:
  StatementWithoutTrailingSubstatement
  LabeledStatementNoShortIf
  IfThenElseStatementNoShortIf
  WhileStatementNoShortIf
  ForStatementNoShortIf

StatementWithoutTrailingSubstatement:
  Block
  EmptyStatement
  ExpressionStatement
  AssertStatement
  SwitchStatement
  DoStatement
  BreakStatement
  ContinueStatement
  ReturnStatement
  SynchronizedStatement
  ThrowStatement
  TryStatement
  YieldStatement

So any of the statements listed can be labeled.

2. You can label the substatement of a statement

Some Java statements contain other statements.

For example, one of the productions for the while statement is the following:

WhileStatement:
  while ( Expression ) Statement

Notice how it contains a substatement.

So the following Java while statement is valid:

while (!interrupted())
  action: execute();

This while statement has a labeled statement as its substatement.

You can do the same with other statements. An if statement:

if (enabled())
  whenTrue: out.println("It is enabled!");
else
  whenFalse: out.println("Not enabled...");

A for statement:

for (int i = 0; i < args.length; i++)
  print: out.println(args[i]);

Which brings us to the labeled statement itself.

3. You can label a labeled statement

So a (sort-of) corollary of items #1 and #2 is that you can label a labeled statement.

The following is valid Java code:

String question;
to: be: or: not: that: is: the: question = "What?";

out.println(question);

The expression statement question = "What?" is labeled with the:. Which in turn is labeled with is:. And so on.

This code compiles. When executed it prints:

What?

4. You cannot label a local variable declaration statement

Go back to the Statement production shown earlier. You may notice it does not define the local variable declaration statement.

The latter is defined a bit earlier in the JLS. Section 14.2 defines blocks and the following productions:

Block:
  { [BlockStatements] }
  
BlockStatements:
  BlockStatement {BlockStatement}
  
BlockStatement:
  LocalClassOrInterfaceDeclaration
  LocalVariableDeclarationStatement
  Statement

As we can see, local variable statements are not Statement but BlockStatement instead. And as they are not Statement they cannot be labeled. Nor can local class or interface declarations.

To illustrate consider the following Java code:

// does not compile!!!

variable: String msg = "Cannot be labeled...";

local: class Local {
  @Override
  public String toString() {
    return msg;
  }
}

It fails to compile:

$ javac src/main/java/post/Item4.java 
src/main/java/post/Item4.java:14: error: variable declaration not allowed here
     variable: String msg = "Cannot be labeled...";
                      ^
src/main/java/post/Item4.java:16: error: class, interface or enum declaration not allowed here
     local: class Local {
            ^
2 errors

To fix the compilation errors, we just need to remove the labels.

Just because you can doesn't mean you should

While the examples given in this article are valid Java code:

So you should not write Java code like this unless you have a good reason to.

It is just an interesting piece of information about the Java language.

Examples from the JDK source code

So how does one use the labeled statement in "real life" code?

Here is an example from the java.util.ArrayList class:

public boolean remove(Object o) {
    final Object[] es = elementData;
    final int size = this.size;
    int i = 0;
    found: {
        if (o == null) {
            for (; i < size; i++)
                if (es[i] == null)
                    break found;
        } else {
            for (; i < size; i++)
                if (o.equals(es[i]))
                    break found;
        }
        return false;
    }
    fastRemove(es, i);
    return true;
}

It labels a block in order to break out of it.

The next example comes from the java.util.Collections class:

nextCand:
    for (int candidate = 0; candidate <= maxCandidate; candidate++) {
        for (int i=0, j=candidate; i<targetSize; i++, j++)
            if (!eq(target.get(i), source.get(j)))
                continue nextCand;  // Element mismatch, try next cand
        return candidate;  // All elements of candidate matched target
    }

In this case there is a for statement nested in another one. The label allows breaking out of the outer loop from the inner loop.

The following is from the java.io.BufferedReader class:

  bufferLoop:
    for (;;) {

      ...

      charLoop:
        for (i = nextChar; i < nChars; i++) {
            c = cb[i];
            if ((c == '\n') || (c == '\r')) {
                if (term != null) term[0] = true;
                eol = true;
                break charLoop;
            }
        }

        ...
    }

So, if I understand it correctly, the labels were used just to make it clear which loop the break refers to. In other words, and in this case, the labels are not strictly required.

If you're interested, I used this program to look for these examples.

Until the next issue of Objectos Weekly

So that's it for today. I hope you enjoyed reading.

The source code of all of the examples are in this GitHub repository.

Please send me an e-mail if you have comments, questions or corrections regarding this post.