1. 導入:なぜパターンの順序が重要なのか
Java 16で導入され、Java 21で完成した「パターンマッチング(instanceofやswitch式)」は、コードの可読性と安全性を飛躍的に高めました。しかし、複数の条件を記述する際、あるパターンが別のパターンを「完全に包含」してしまうケースが存在します。これを「Dominance(優位性)」と呼びます。この順序を誤ると、コンパイルエラーが発生し、プログラムが意図通りに動作しません。本記事では、この仕様を正しく理解し、安全なコードを書くための指針を解説します。
2. 基礎知識:パターンとDominanceの仕組み
Javaのパターンマッチングにおいて、あるパターンAが別のパターンBを包含している場合、「AはBに対して優位(Dominant)である」と言います。
例えば、Object型はString型を包含しているため、Object型を判定するパターンはString型に対して優位です。もし「子クラスの判定」よりも先に「親クラスの判定」を記述してしまうと、子クラスのケースは永遠に到達不能となり、Javaコンパイラはこれを不整合としてエラーにします。
3. 実装/解決策:具体的なルール
パターンマッチングにおける鉄則は、「特殊なもの(サブタイプ)から先に書き、一般的なもの(スーパータイプ)を後に書く」ことです。
switch文で型による分岐を行う際は、具体的なクラスを上に、Object型やdefaultラベルを下に配置するように意識してください。
4. サンプルプログラム
以下は、instanceofによるパターンマッチングとswitch式を組み合わせた、安全な実装例です。
public class PatternMatchingDemo {
public static void main(String[] args) {
Object obj = “Java 21 Pattern Matching”;
// switch式でのパターンマッチング例
String result = switch (obj) {
// 特殊なケースを先に記述(StringはCharSequenceのサブタイプなので先に書く)
case String s -> “文字列です: ” + s.toUpperCase();
// 次に汎用的なインターフェースを記述
case CharSequence cs -> “文字列以外のシーケンス: ” + cs.length();
// 最後に最も一般的な型やnullを記述
case Integer i -> “数値です: ” + i;
case null, default -> “対象外または不明な型です”;
};
System.out.println(result);
}
}
5. 応用・注意点:現場で陥りやすいバグの回避策
現場で注意すべきは、以下の2点です。
・ガード条件(when句)の過信
Java 21からのswitch式では、`case String s when s.length() > 10` のようにガード条件を付与できます。しかし、ガード条件があっても「型としての優位性」は変わりません。型が包含関係にある場合、ガード条件の有無に関わらず、優位性ルールに従う必要があります。
・継承関係の変更による事故
将来的に既存クラスの継承関係を変更した場合、これまでエラーにならなかったswitch文で「Dominance」のコンパイルエラーが発生することがあります。レガシーコードをリファクタリングする際は、型階層図を意識し、順序が逆転していないか再確認してください。
常に「具体的な型から汎用的な型へ」という順序を守ることで、コンパイラの恩恵を最大限に受け、バグを未然に防ぐ堅牢なコードを維持しましょう。

コメント