JEP 361: Switch Expressions (Standard)
Java 12でプレビュー(JEP 361)として導入された、Switch Expressions が Java 14 で正式導入となりました。
以下のように switch を文ではなく、式として扱えるようになります。
int numLetters = switch (day) { case MONDAY, FRIDAY, SUNDAY -> 6; case TUESDAY -> 7; case THURSDAY, SATURDAY -> 8; case WEDNESDAY -> 9; };
従来型の case L :
形式に加え、Arrow labels と呼ばれる case L ->
形式がサポートされ、マッチング条件はカンマで複数指定できるようになります。
Arrow labels の右辺のアームには、式、ブロック、throwステートメントに限定されます。旧来の switch では case の中で変数定義した場合にスコープが switch 全体に及びましたが、Arrow labels では単一式か明示的なブロックしか定義できないため、この分かりにくさが解消されています。
Java における 当初の switch 文は C や C++ から流用されており、整数型の値に対してしか利用できませんでした。
Java 1.5 より、列挙が使用できるようになり、Java 1.7 より文字列が利用できるようになりました。
そして今回の Java 14 では将来のより柔軟なパターンマッチの布石となる Switch Expressions が導入されました。
フォールスルー
Java における switch文は C や C++ などの言語を踏襲しており、フォールスルー(fall through)をサポートします。
つまり break
を書かない場合に次の case にフローが移ります。この制御フローは低レベルの処理には便利な場面がありますが、高レベルのコンテキストにおいてはエラーを埋め込み易いという問題があります。
switch (day) { case MONDAY: case FRIDAY: case SUNDAY: System.out.println(6); break; case TUESDAY: System.out.println(7); break; case THURSDAY: case SATURDAY: System.out.println(8); break; case WEDNESDAY: System.out.println(9); break; }
Arrow labels では複数の条件をカンマ区切りで記載できるため、以下のように視認性の高い記述が可能となります。
switch (day) { case MONDAY, FRIDAY, SUNDAY -> System.out.println(6); case TUESDAY -> System.out.println(7); case THURSDAY, SATURDAY -> System.out.println(8); case WEDNESDAY -> System.out.println(9); }
列挙型の switch 式の場合は、条件の網羅性がチェックされるようになり安全性が向上しました。
例えば前述の例で WEDNESDAY
の条件が不足していた場合はコンパイルエラーになります。
文字列や整数型に対する switch 式の場合は default の定義が無い場合にコンパイルエラーになります。
Switch 式
前述の通り、switch が値を返すことが出来るようになりました。
String s = switch (k) { case 1 -> "one"; case 2 -> "two"; default -> "many"; };
switch 式は複合式であり、型が規定された場合はその型が強制されますが、規定されない場合は各アームのタイプを組み合わせて判断されます。
System.out.println( switch (k) { case 1 -> 1; case 2 -> "two"; default -> BigInteger.ZERO; } );
値の生成
switch 式のアームにはたいてい単一の式を使いますが、ブロックが必要な場合には yield ステートメントにて値を生成します。
int j = switch (day) { case MONDAY -> 0; case TUESDAY -> 1; default -> { int k = day.toString().length(); int result = f(k); yield result; } };
yield ステートメントは旧来型の switch ブロックから値を生成する場合にも利用できます。
int result = switch (s) { case "Foo": yield 1; case "Bar": yield 2; default: System.out.println("Neither Foo nor Bar, hmmm..."); yield 0; };
この場合にも条件の網羅性のチェックが行われます。
まとめ
Java 14 で正式導入となった Switch 式について説明しました。
他の現代的な言語に比べると、パターンマッチ機能はまだまだ貧弱ではありますが、Java 15 で予定される JEP draft: Pattern matching for switch (Preview) への土台となるものであり歓迎するものです。
将来的には以下の記載も可能になりますし、より高度なパターンマッチがも可能になる予定です。
String formatted = switch (obj) { case Integer i -> String.format("int %d", i) case Byte b -> String.format("byte %d", b); case Long l -> String.format("long %d", l); case Double d -> String.format("double %f", d); case String s -> String.format("String %s", s); default -> obj.toString(); };