Java 16で正式に導入された、instanceof演算子のパターンマッチング機能(Pattern Matching for instanceof)は、型チェックとキャストを一括で処理できる構文です。コードをよりシンプルかつ安全に書くための機能ですが、仕組みや使い方に戸惑う方も多いかもしれません。
特にJava Silver試験などでよく問われる内容でもあり、今後徐々に利用頻度が増えていくと思われる重要構文の1つです。現場で活躍できるJavaプログラマーを目指す方であれば知っておきたい基本知識の1つです。
このページではパターンマッチング機能について1からわかりやすく解説します。是非最後までご覧ください。
【前提】instanceofでの判定とキャスト
Java 15以前では、あるオブジェクトが特定の型かを判定し、それを利用しようとする場合、次のように「判定 → キャスト」の2段階を踏むのが一般的でした。
Object obj = "Hello World"; // ここでは実際は String 型
if (obj instanceof String) {
    // まず instanceof で判定
    String s = (String) obj; // キャストを明示的に書く
    System.out.println(s.toUpperCase());
}
- if (obj instanceof String)で型チェック (- String型かどうか)
- String s = (String) obj;でキャスト
- s.toUpperCase()のようにメソッド呼び出し
このように、判定とキャストがそれぞれ別々の箇所に存在し、キャストが常に明示的に必要という点がやや煩わしいところでした。
参考 キャストとは?
パターンマッチング付きinstanceofの基本構文
Java 16以降では、上記処理をより簡潔に記述できるようになりました。
Object obj = "Hello World";
if (obj instanceof String s) {
    // 'instanceof String s' で判定とキャストを同時に行う
    System.out.println(s.toUpperCase());
}
この書き方では、
パターンマッチング機能のメリット
- コードがスッキリ
- 従来のように毎回キャストを書く必要がなくなり、可読性が向上します。
 
- スコープの明確化
- sという変数は- ifブロック内のみ有効になるため、意図しない範囲で使われる可能性がありません。
 
- 安全性の向上
- instanceofの結果が- trueだった場合だけパターン変数が使えるので、- ClassCastExceptionのリスクを意識しなくて済みます。
 
パターン変数のスコープと使い方
パターンマッチングを行った変数(上の例だと s)の有効範囲(スコープ)は、instanceofのチェックが行われたブロック内に限られます。↓のコードを見てみましょう。
Object obj = "Hello World";
if (obj instanceof String s) {
    // ここでは s は String 型として使える
    System.out.println("Upper case: " + s.toUpperCase());
} else {
    // ここでは s は使えない
    // System.out.println(s.toLowerCase()); // コンパイルエラーになる
}
- ifブロック内では、変数- sは- String型として使用できます。
- elseブロックや- ifブロックを抜けた後では、- sは「存在しない」扱いなので使用できません。

これは一見不便に思えるかもしれませんが、「その型だと判明している場所だけでしか使わない」という意図が明確になるため、可読性と安全性が高まります。
論理演算子(AND/ORなど)と組み合わせる場合
Java 16で導入されたパターンマッチングは「論理演算子」と組み合わせて書く場合が特に強力です。よくある例として、&&(AND)を使って条件がtrueになった場合のみ追加チェックをしたいケースがあります。
Object obj = "Hello World";
if (obj instanceof String s && !s.isEmpty()) {
    // obj が String にマッチし、かつ s が空文字列ではない場合
    System.out.println("Lower case: " + s.toLowerCase());
}
- obj instanceof String sが- trueのときだけ変数- sが- String型として扱える
- その結果をさらに使って !s.isEmpty()をチェック
ここでのポイントは、instanceofがtrueでないと&&の右側は評価されない(短絡評価)ため、sを安全に使うことができます。(左側がfalseの場合は右側にたどり着かないので、そもそもStringとしての操作が行われず、ClassCastExceptionなどの心配もありません。)
なぜ「パターンマッチング」と呼ばれるのか
「パターンマッチング」という用語は、たとえばScalaやKotlinなどではmatch式やwhen式として、型や値のパターンをマッチさせて分岐処理を簡潔に記述できる機能を指します。Javaのinstanceofへの拡張は「パターンマッチングを導入する」流れの一部で、将来的にはswitch文などでもパターンマッチングを活用できる機能が拡充されています(Java 17以降でプレビュー機能として提供)。
instanceof String s は、
- 「もし objがString型というパターンにマッチしたならば」
- 「そのマッチ対象を sという変数として使えるようにする」
という発想で書かれています。これが「パターンマッチング」と呼ばれる理由です
if-else if-else構文との併用
複数の型の可能性をチェックするケースも、よりスマートに書けます。
Object obj = Integer.valueOf(100);
if (obj instanceof String s) {
    System.out.println("It's a String: " + s.toUpperCase());
} else if (obj instanceof Integer i) {
    System.out.println("It's an Integer: " + (i + 10));
} else {
    System.out.println("Unknown type.");
}
// It's an Integer: 110
objがそれぞれどの型にマッチしたかで場合分けできますし、マッチしたブロック内では常に該当の型として安全に操作できます。
ネストした条件分岐
パターン変数が使えるのは、そのif文や、論理演算子でつながった同一の評価式内になります。
Object obj = "Nested Checking";
if (obj instanceof String s) {
    if (s.length() > 5) {
        System.out.println("String is more than 5 characters");
    }
}
// String is more than 5 characters
このようにネストしていくことも可能ですが、論理演算子(&&や||)を使ってまとめて書く方法も便利です。

Java 16以降を使える環境であれば、このパターンマッチングを活用してみることをおすすめします。条件分岐やキャスト周りのコードが大幅にシンプルになり、可読性と保守性が向上するはずです。ぜひ活用してみてください!

