6 switch式

あらゆる式と同様、switch式は1つの値に評価され、文での使用が可能です。フォール・スルーを防ぐためのbreak文が不要になるcase L ->ラベルを使用できます。switch式の値の指定には、yield文を使用します。

switch式の設計に関する背景情報は、JEP 361を参照してください。

case L ->ラベル

曜日の文字数を出力する次のswitch文について検討してみます。

public enum Day { SUNDAY, MONDAY, TUESDAY,
    WEDNESDAY, THURSDAY, FRIDAY, SATURDAY; }

// ...

    int numLetters = 0;
    Day day = Day.WEDNESDAY;
    switch (day) {
        case MONDAY:
        case FRIDAY:
        case SUNDAY:
            numLetters = 6;
            break;
        case TUESDAY:
            numLetters = 7;
            break;
        case THURSDAY:
        case SATURDAY:
            numLetters = 8;
            break;
        case WEDNESDAY:
            numLetters = 9;
            break;
        default:
            throw new IllegalStateException("Invalid day: " + day);
    }
    System.out.println(numLetters);

曜日名の長さは、変数numLettersに格納するかわりに値を戻せた方が便利で、これはswitch式を使用して行うことができます。また、フォール・スルーを防ぐのに、記述が面倒で付け忘れがちなbreak文が必要がなくなれば、さらに便利です。これは、新しい種類のcaseラベルで実現できます。次のswitch式では、新しい種類のcaseラベルを使用して、曜日の文字数を出力します。

    Day day = Day.WEDNESDAY;    
    System.out.println(
        switch (day) {
            case MONDAY, FRIDAY, SUNDAY -> 6;
            case TUESDAY                -> 7;
            case THURSDAY, SATURDAY     -> 8;
            case WEDNESDAY              -> 9;
            default -> throw new IllegalStateException("Invalid day: " + day);
        }
    );    

新しい種類のcaseラベルの形式は次のとおりです。

case label_1, label_2, ..., label_n -> expression;|throw-statement;|block 

Javaランタイムが矢印の左側にある任意のラベルに一致すると、矢印の右側にあるコードが実行されて、フォール・スルーしません。つまり、switch式(または文)内の他のコードは実行されません。矢印の右側のコードが式である場合、その式の値はswitch式の値になります。

新しい種類のcaseラベルをswitch文で使用できます。次の例は最初の例と同様ですが、case L:ラベルのかわりにcase L ->ラベルが使用されています:

    int numLetters = 0;
    Day day = Day.WEDNESDAY;
    switch (day) {
        case MONDAY, FRIDAY, SUNDAY -> numLetters = 6;
        case TUESDAY                -> numLetters = 7;
        case THURSDAY, SATURDAY     -> numLetters = 8;
        case WEDNESDAY              -> numLetters = 9;
        default -> throw new IllegalStateException("Invalid day: " + day);
    };
    System.out.println(numLetters);

case L ->ラベルとその右側のコードは、switchラベル付きルールと呼ばれます。

case L:文とyield文

switch式の中にcase L:ラベルを使用できます。case L:ラベルとその右側のコードは、switchラベル付き文グループと呼ばれます。

    Day day = Day.WEDNESDAY;
    int numLetters = switch (day) {
        case MONDAY:
        case FRIDAY:
        case SUNDAY:
            System.out.println(6);
            yield 6;
        case TUESDAY:
            System.out.println(7);
            yield 7;
        case THURSDAY:
        case SATURDAY:
            System.out.println(8);
            yield 8;
        case WEDNESDAY:
            System.out.println(9);
            yield 9;
        default:
            throw new IllegalStateException("Invalid day: " + day);
    };
    System.out.println(numLetters);

前の例ではyield文が使用されています。引数を1つ取りますが、それはcaseラベルがswitch式で生成する値です。

yield文により、switch文とswitch式を容易に区別できるようになります。switch文(switch式ではない)は、break文のターゲットとすることができます。反対に、switch式(switch文ではない)は、yield文のターゲットとすることができます。

ノート:

case L ->ラベルの使用をお薦めします。case L:ラベルの使用時は、break文またはyield文の挿入を忘れがちです。これを忘れると、コード内で思いがけないフォール・スルーが発生する場合があります。

case L ->ラベルで、複数の文または式でないコード、あるいはthrow文を指定するには、それらをブロック内に囲みます。caseラベルが生成する値をyield文で指定します。

    int numLetters = switch (day) {
        case MONDAY, FRIDAY, SUNDAY -> {
            System.out.println(6);
            yield 6;
        }
        case TUESDAY -> {
            System.out.println(7);
            yield 7;
        }
        case THURSDAY, SATURDAY -> {
            System.out.println(8);
            yield 8;
        }
        case WEDNESDAY -> {
            System.out.println(9);
            yield 9;
        }
        default -> {
            throw new IllegalStateException("Invalid day: " + day);
        }
    };  

網羅性

switch文とは異なり、switch式は網羅的であることが求められるため、考えられるすべての値に対して、一致するswitchラベルが必要です。そのため、switch式には通常default句が必要です。ただし、既知の定数をすべてカバーする列挙switch式については、コンパイラによって暗黙的なdefault句が挿入されます。

また、switch式は、値で正常に完了するか、例外をスローして完了する必要があります。たとえば、次のコードは、switchラベル付きルールにyield文がないため、コンパイルされません:

int i = switch (day) {
    case MONDAY -> {
        System.out.println("Monday"); 
        // ERROR! Block doesn't contain a yield statement
    }
    default -> 1;
};

次の例は、switchラベル付き文グループにyield文がないため、コンパイルされません:

i = switch (day) {
    case MONDAY, TUESDAY, WEDNESDAY: 
        yield 0;
    default: 
        System.out.println("Second half of the week");
        // ERROR! Group doesn't contain a yield statement
};
switch式は1つの値に評価される(または例外をスローする)必要があるため、次の例のように、breakyieldreturnまたはcontinue文を使用してswitch式をジャンプすることはできません:
z: 
    for (int i = 0; i < MAX_VALUE; ++i) {
        int k = switch (e) { 
            case 0:  
                yield 1;
            case 1:
                yield 2;
            default: 
                continue z; 
                // ERROR! Illegal jump through a switch expression 
        };
    // ...
    }