パターンマッチング付き instanceof を一言でいうと
「instanceof で型を調べたあと、すぐ下でキャストして変数に代入する」――この“二度書き”をまとめて一発で書けるようにした構文が、パターンマッチング付き instanceof です。
if (obj instanceof String s) のように書くと、
「obj が String かどうか判定し、true のときだけ s という String 変数が使える」
という形になります。
型チェックとキャストを同時にやってくれるので、コードが短くなり、キャストミスのバグも減ります。
従来の instanceof が抱えていた「二度書き」の問題
古い書き方:判定とキャストがバラバラ
昔ながらの instanceof は、必ずこういう形になっていました。
Object obj = ...;
if (obj instanceof String) {
String s = (String) obj; // 明示的なキャストが必要
System.out.println(s.toUpperCase());
}
Javaやりたいことは「obj が String なら、大文字にして表示したい」だけなのに、
instanceofで型を判定する- その直後に、同じ型名でキャストを書く
という“同じ情報の二度書き”が必要でした。
しかも、キャストを書き忘れたり、間違った型にキャストしたりすると、実行時エラーの原因になります。
パターンマッチング付き instanceof の基本構文
型チェックと変数宣言を一度に書く
新しい書き方はこうです。
Object obj = ...;
if (obj instanceof String s) {
System.out.println(s.toUpperCase());
}
Javaobj instanceof String s の部分を分解すると、
obj instanceof Stringで「objがStringか?」を判定trueのときだけ、String s = (String) obj;が自動で行われる
というイメージです。
if のブロックの中では、すでに s は String 型として扱えるので、
キャストを書く必要がありません。
もう少し複雑な例
void printLength(Object obj) {
if (obj instanceof String s) {
System.out.println("length = " + s.length());
} else {
System.out.println("not a String");
}
}
Javaobj が String のときだけ s.length() が呼べて、
それ以外の型のときは else に流れます。
「型チェック → キャスト → 変数宣言」という一連の流れが、instanceof String s の一箇所にまとまっているのがポイントです。
パターン変数のスコープ(どこまで使えるか)
基本は「if の中だけ」
次のコードを見てください。
if (obj instanceof String s) {
System.out.println(s.toUpperCase());
}
System.out.println(s); // ここはコンパイルエラー
Javas は「パターン変数」と呼ばれ、if の条件が true になったときのブロック内でだけ有効です。
つまり、
ifの中ではsをStringとして使えるifの外には出てこない
というスコープのルールになっています。
else-if でも同じように使える
if (obj instanceof String s) {
System.out.println("String: " + s);
} else if (obj instanceof Integer i) {
System.out.println("Integer: " + i);
} else {
System.out.println("other");
}
Javaこの場合、
- 最初の
ifブロックではsが有効 else ifブロックではiが有効- それぞれのブロックの外ではどちらも使えない
というふうに、ブロックごとにパターン変数が閉じています。
論理演算子(&& / ||)と組み合わせたときの挙動
&&(AND)と組み合わせるとき
if (obj instanceof String s && s.length() > 3) {
System.out.println(s.toUpperCase());
}
Javaここでは、
- まず
obj instanceof String sが評価される trueならsが有効になり、s.length() > 3が評価される
という順番になります。
&& の左側が true になったときだけ右側が評価される、という Java のルールのおかげで、
右側では安全に s を使えます。
||(OR)と組み合わせるときの注意
if (obj instanceof String s || s == null) {
// ここで s を使おうとすると危険
}
Javaこれは危ない書き方です。
|| の場合、左側が false のときに右側が評価されます。
つまり、obj instanceof String s が false なら、s はまだ存在していないのに s == null を評価しようとしてしまいます。
コンパイラもこれを許さず、
「その位置では s は確実に初期化されていない可能性がある」としてエラーにします。
実務では、
&&と組み合わせるのは OK(左が true のときだけ右が評価される)||と組み合わせるときは、パターン変数を右側で使わない
という感覚を持っておくと安全です。
sealed class と組み合わせた「きれいな型分岐」
sealed 階層を前提にした instanceof
例えば、こんな sealed 階層があるとします。
sealed interface Shape permits Circle, Rectangle {}
final class Circle implements Shape {
double radius;
Circle(double radius) { this.radius = radius; }
}
final class Rectangle implements Shape {
double width, height;
Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
}
Javaこれに対して、instanceof のパターンマッチングを使うと、
かなりスッキリした分岐が書けます。
double area(Shape shape) {
if (shape instanceof Circle c) {
return Math.PI * c.radius * c.radius;
} else if (shape instanceof Rectangle r) {
return r.width * r.height;
} else {
throw new IllegalArgumentException("unknown shape");
}
}
JavaCircle のときは c、Rectangle のときは r として扱えます。
キャストを書かなくていいので、
「どの型のときに何をしているか」が素直に読めます。
どんなときに「パターンマッチング付き instanceof」を使うべきか
「型ごとに処理を分けたい」場面で、まず候補にする
典型的には、次のような場面です。
Objectやインターフェースを受け取って、実際の具象型ごとに処理を変えたい- sealed class のサブタイプごとにロジックを分けたい
- 既存のコードで「instanceof → キャスト → 変数宣言」が並んでいて、読みにくい
こういうところを見つけたら、
if (x instanceof SomeType t) {
// t を使う
}
Javaという形に書き換えられないかを考えてみるといいです。
「キャストを書かない」ことが、そのまま安全性につながる
手でキャストを書くと、
- 間違った型にキャストしてしまう
instanceofの条件とキャストの型がズレてしまう
といったバグが入り込みます。
パターンマッチング付き instanceof では、
「判定に使った型」と「変数の型」が必ず一致するので、
この手のミスをそもそも書けなくなります。
まとめ:パターンマッチング付き instanceof を自分の言葉で説明するなら
あなたの言葉でまとめると、こうなります。
「パターンマッチング付き instanceof は、if (obj instanceof String s) のように書くことで、
型チェックとキャストと変数宣言を一度に行える構文。true のブロック内では s をその型として安全に使え、
キャストを書く必要がなくなる。
パターン変数のスコープは“条件が真のブロック内だけ”で、&& との組み合わせでは自然に使えるが、|| では注意が必要。
型ごとに処理を分けるコードを短く、安全に書けるようにするための、
モダンな instanceof の書き方。」
