_if/else_文と同じように、switch文を使うと、条件に応じてコードを実行することで、プログラムの流れを変えられます。
違いは、switch文では、プリミティブ型または文字列の式の値を、あらかじめ定義された定数値としか比較できないという点です。
switch文を使うときには、いくつかのキーワードが役立ちます。
switch: このキーワードで、switchの構造を宣言します。
そのあとには、結果を変化させる式または変数を書きます。case: これは、結果として取りうる複数の可能性を宣言するために使います。break: breakキーワードは、意図した流れの最後でswitchの実行を止めるためにとても役立ちます。
これを忘れると、プログラムはそのまま続けて実行され、予期しない結果になることがあります。default: 名前のとおり、式の結果がほかのどのcaseにも一致しないときの既定の結果として使います。最も単純な場合、プリミティブ型または文字列の式を評価し、その値に基づいて判断を下します。 たとえば、次のとおりです。
String direction = getDirection();
switch (direction) {
case "left":
goLeft();
break;
case "right":
goRight();
break;
default:
// otherwise
markTime();
break;
}
Java 14以降(それ以前のJava 12と13ではプレビューとして利用できました)では、「強化された」switchの実装を使えます。
1つのcaseに複数の値を指定できます。
従来のswitch文では、フォールスルーを使えます。次の例では、case 1とcase 3が同じ処理を実行します。
これは、case 1がbreakキーワードを使っていないためです。
switch (number) {
case 1:
case 3:
// do same stuff
break;
case 2:
// do different stuff
break;
// (...)
}
強化されたswitch expressionでは、1つのcaseに直接複数の値を指定できます。
次の例を見てください。
switch (number) {
case 1, 3:
// do stuff
break;
case 2:
// do other stuff
break;
// (...)
}
これで、switch-statementまたはswitch expressionを書けるようになりました。
両者の違いは何でしょうか? 基本的に、文は厳密なロジックを前提とし、式は値を返すことができます。
次のように書く代わりに、
String result = "";
switch (expression) {
case "bob":
result = "bob";
break;
// (...)
}
このように書けます。
String result = switch (expression) {
case "bob":
yield "bob";
// (...)
}
yieldはreturnと同じように働きますが、switch式のためのものです。
yieldは式を終了させるので、ここではbreakは必要ありません。
_switch文_と_switch式_のもう一つの違いは、switch式_では必ず_すべてのケースを網羅しなければならないことです。
すべての取りうる値にcaseを用意するか、defaultケースを使うかのどちらかです。
:の代わりに->を使えます。
->を使うと、breakキーワードを書かなくて済みます。
どちらの記法も使えますが、1つのswitchの中ではどちらか一方だけを使わなければなりません。
switch(expression) {
case 1 -> yield "one"
case 2 -> yield "two"
default: yield "other number" // Removing this will result in a compile error
}
スコープです。
従来のswitchでは、switch全体でスコープが1つしかないため、そのスコープに起因する予期しない動作が起こることがあります。
switch(expression) {
case 1:
String message = "something";
break;
case 2:
String message = "anything";
break;
// (...)
}
この例が動作しないのは、switchの中でmessageが2回宣言されているからです。
次のようにすれば解決できます。
switch (expression) {
case 1: {
String message = "something";
break;
}
case 2: {
String message = "anything";
break;
}
// (...)
}
{}がcaseのスコープを区切っているからです。
ただし、{}は必須ではないので直感的とは言えません。
一方、新しい->記法を使う場合は、そのあとに、単一の文または式、throw文、{}ブロックのいずれかを必ず続けなければなりません。
もう混乱することはありません!
強化されたswitchの詳細については、こちらの記事とこちらの記事、そして公式のOracleのドキュメントを参照してください。
さらに、Java 21ではGuarded Patternsという機能が追加され、caseラベル自体でチェックを行えるようになりました。
String dayOfMonth = getDayOfMonth();
String day = "";
return switch (day) {
case "Tuesday" when dayOfMonth == 13 -> "Forbidden day!!";
case "Monday", "Tuesday", "Wednesday", "Thursday", "Friday" -> "Week day";
case "Saturday", "Sunday" -> "Weekend";
default -> "Unknown";
};
Java 21のswitch式についての詳細は、こちらのブログで確認できます。