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式のcaseは網羅的であることが求められるため、考えられるすべての値にswitchラベルが一致する必要があります。そのため、switch式には通常default句が必要です。ただし、既知の定数をすべて網羅するenumの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つの値に評価される(または例外をスローする)必要があるため、次の例のように、break、yield、returnまたは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
};
// ...
}
switch式の網羅性
ノート:
この機能は、プレビュー機能であるJEP 420の一部です。パターンやnullラベルが使用されている場合や、セレクタ式がレガシー・タイプ(char、byte、short、int、Character、Byte、Short、Integer、Stringまたはenumタイプ)のいずれかでない場合、switch文のcaseは網羅的であることが必要です。
次の例は、switch文(パターン・ラベルが使用されている)が網羅的でないため、コンパイルされません:
static void testSwitchStatementExhaustive(Object obj) {
switch (obj) { // error: the switch statement does not cover
// all possible input values
case String s:
System.out.println(s);
break;
case Integer i:
System.out.println("Integer");
break;
}
}
default句を追加すると、網羅的になります:
static void testSwitchStatementExhaustive(Object obj) {
switch (obj) {
case String s:
System.out.println(s);
break;
case Integer i:
System.out.println("Integer");
break;
default:
break;
}
}