Java 16で正式に導入された、instanceof
演算子のパターンマッチング機能(Pattern Matching for instanceof)は、型チェックとキャストを一括で処理できる構文です。コードをよりシンプルかつ安全に書くための機能ですが、仕組みや使い方に戸惑う方も多いかもしれません。
このページではパターンマッチング機能について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()); }
この書き方では、
obj instanceof String s
が「パターン」にあたります。String s
と書くことで、obj
がString
型の場合に、自動的にその型で扱うための変数s
を用意してくれます。- 実際には
(String)obj
をする必要がなくなり、冗長なキャストが省略できます。
obj
がString
型でない場合はif
文の条件がfalse
となるので、ブロック内部は実行されません。
パターンマッチング機能のメリット
- コードがスッキリ
- 従来のように毎回キャストを書く必要がなくなり、可読性が向上します。
- スコープの明確化
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."); }
obj
がそれぞれどの型にマッチしたかで場合分けできますし、マッチしたブロック内では常に該当の型として安全に操作できます。
ネストした条件分岐
パターン変数が使えるのは、そのif
文や、論理演算子でつながった同一の評価式内になります。
Object obj = "Nested Checking"; if (obj instanceof String s) { if (s.length() > 5) { System.out.println("String is more than 5 characters"); } }
このようにネストしていくことも可能ですが、論理演算子(&&
や||
)を使ってまとめて書く方法も便利です。

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