ループ以外の文にラベルは付けられるか?


問題

Javaにおけるラベルは通常、ループ文(do文、for文、while文)に付けられる。

LOOP1:for (String str1 : list1) { // ラベルを付けたfor文
    for (String str2 : list2) {
        if (str1.equals(str2)) {
            found(str1);
            break LOOP1; // 外側のforから抜ける
        }
    }
}

それでは、ループ文以外の文にラベルを付けることはできるか?

  1. ループ文以外の文にラベルを付けることはできない(コンパイルエラーになる)。
  2. ループ文以外の文にラベルを付けられる場合もあるが、そのラベルを参照する文が書けないので結局無意味である。
  3. ループ文以外の文にラベルを付けられる場合があり、かつ、そのラベルを参照する文が書ける場合もある。

正解

3. ループ文以外の文にラベルを付けられる場合があり、かつ、そのラベルを参照する文が書ける場合もある。

解説

Javaのほとんどの文にはラベルを付けられる。

// ラベル付式文!
FOO: a = 42; // OK
// ラベル付if文!
BAR: if (a > 0) b = 1;
else b = -1; // OK

※ただし変数宣言文などラベルが付けられない文もある1

コンパイルエラー
// ラベル付変数宣言文??
FOO: int a = 42; // NG!

そして、ブロック文およびブロック文を従属させた制御文(ループに限らず、if文やtry文等も含む)にラベル(例えば LABEL:)が付けられた場合、そのブロックの中で break LABEL; を実行することで LABEL: を付けた文から脱出することができる。

FOO: if (val > 1000) { // ラベル付if文!
    big = true;
    if (val % 2 == 0) break FOO; // ブロックを抜ける
    bigOdd = true;
}

例えば、関数の中の特定の領域に対して“早期脱出”をしたい、という場面で活用できるだろう。

BLOCK:{ // 素のブロック
    Foo foo1 = stor.findFoo(fooName1);
    if (foo1 == null)
        break BLOCK; // ブロックを抜ける
    Foo foo2 = stor.findFoo(fooName2);
    if (foo2 == null)
        break BLOCK; // ブロックを抜ける

    foo2.setBar(foo1.getBar());
    // 以降処理が延々と続く...
}

補足

  • ラベル無しのbreak文の対象になるのはループ文とswitch文に限られる。
  • continue文の対象になるのは、ラベルの有無に関わらず、ループ文に限られる。

蛇足

実は、ブロックのない文に付けたラベルであっても、それを参照するbreak文は書ける、らしい。

FOO: break FOO; // OK!!!!

まあ、全く有用性はないのだが……。


  1. Java言語仕様にある形式定義において、“Statement”に相当する文にラベルが付けられる。局所変数宣言文(“LocalVariableDeclarationStatement”)は“Statement”の一種ではない。