導入
皆さんは日常のコーディングで、複雑なif-else文に頭を抱えたことはありませんか?特に文字列の条件分岐が重なると、コードの可読性は著しく低下します。Java 7から導入された「Stringのswitch対応」を皮切りに、近年のJavaではswitch文が劇的な進化を遂げています。本稿では、レガシーなswitch文から、Java 17以降で実務に不可欠となった「switch式」や「sealed classes」との連携までを解説します。これらを習得することで、堅牢でクリーンな制御フローを実現しましょう。
基礎知識
かつてJavaのswitch文で扱える型は、intやchar、enumといった数値的・列挙的な型のみでした。しかし、Java 7でStringがサポートされたことで、文字列比較を伴う条件分岐を非常に簡潔に書けるようになりました。
さらに、Java 14/17で登場した「switch式(switch expressions)」は、値を返すことが可能です。これにより、「値を代入するための変数を用意して、各caseで代入する」というボイラープレートコードを排除できます。
実装/解決策
実務で重要なのは、網羅性(Exhaustiveness)の担保です。Java 17以降のswitch式では、すべてのパターンが網羅されていないとコンパイルエラーになるため、バグを未然に防げます。また、sealed classes(封印されたクラス)と組み合わせることで、特定の型以外は受け付けない強力な型安全な分岐が可能になります。
サンプルプログラム
以下のコードは、最新のswitch式とsealed classesを組み合わせた実用的な例です。
// 許可されたサブクラスのみを定義
sealed interface Operation permits Add, Subtract {}
record Add(int a, int b) implements Operation {}
record Subtract(int a, int b) implements Operation {}
public class SwitchExample {
public static void main(String[] args) {
Operation op = new Add(10, 5);
// switch式により結果を直接変数に代入
int result = switch (op) {
case Add add -> add.a() + add.b();
case Subtract sub -> sub.a() - sub.b();
// sealed classのおかげで、default句なしで網羅性が保証される
};
System.out.println("計算結果: " + result);
// 従来のString比較の例
String command = "START";
String status = switch (command) {
case "START" -> "稼働中";
case "STOP" -> "停止中";
default -> "不明"; // 文字列の場合は網羅性が不明なためdefaultが必要
};
System.out.println("ステータス: " + status);
}
}
応用・注意点
現場で活用する際のポイントをまとめます。
1. yieldの利用
switch式の中で複雑なロジックを書きたい場合は、アロー演算子(->)ではなく、ブロック{}とyieldキーワードを使用します。これにより、case内での一時的な計算が容易になります。
2. nullの扱いに注意
最新のswitch式では、パターンマッチングの際にnullをcaseに含めることも可能です。予期せぬNullPointerExceptionを防ぐため、特定のcaseでnullチェックを行うか、defaultで処理する習慣をつけましょう。
3. 既存コードの移行
レガシーなswitch文を無理にすべて書き換える必要はありませんが、複雑なif-elseが連鎖している箇所は、積極的にswitch式へのリファクタリングを推奨します。特にsealed classesとの組み合わせは、ビジネスロジックの変更に対して非常に強い耐性を持つコードを生み出します。

コメント