Java | Java 詳細・モダン文法:言語仕様詳細 – パターンマッチング(instanceof)

Java Java
スポンサーリンク

パターンマッチング付き instanceof を一言でいうと

instanceof で型を調べたあと、すぐ下でキャストして変数に代入する」――この“二度書き”をまとめて一発で書けるようにした構文が、パターンマッチング付き instanceof です。

if (obj instanceof String s) のように書くと、
objString かどうか判定し、true のときだけ s という String 変数が使える」
という形になります。

型チェックとキャストを同時にやってくれるので、コードが短くなり、キャストミスのバグも減ります。


従来の instanceof が抱えていた「二度書き」の問題

古い書き方:判定とキャストがバラバラ

昔ながらの instanceof は、必ずこういう形になっていました。

Object obj = ...;

if (obj instanceof String) {
    String s = (String) obj;  // 明示的なキャストが必要
    System.out.println(s.toUpperCase());
}
Java

やりたいことは「objString なら、大文字にして表示したい」だけなのに、

  1. instanceof で型を判定する
  2. その直後に、同じ型名でキャストを書く

という“同じ情報の二度書き”が必要でした。
しかも、キャストを書き忘れたり、間違った型にキャストしたりすると、実行時エラーの原因になります。


パターンマッチング付き instanceof の基本構文

型チェックと変数宣言を一度に書く

新しい書き方はこうです。

Object obj = ...;

if (obj instanceof String s) {
    System.out.println(s.toUpperCase());
}
Java

obj instanceof String s の部分を分解すると、

  • obj instanceof String で「objString か?」を判定
  • true のときだけ、String s = (String) obj; が自動で行われる

というイメージです。

if のブロックの中では、すでに sString 型として扱えるので、
キャストを書く必要がありません。

もう少し複雑な例

void printLength(Object obj) {
    if (obj instanceof String s) {
        System.out.println("length = " + s.length());
    } else {
        System.out.println("not a String");
    }
}
Java

objString のときだけ s.length() が呼べて、
それ以外の型のときは else に流れます。

「型チェック → キャスト → 変数宣言」という一連の流れが、
instanceof String s の一箇所にまとまっているのがポイントです。


パターン変数のスコープ(どこまで使えるか)

基本は「if の中だけ」

次のコードを見てください。

if (obj instanceof String s) {
    System.out.println(s.toUpperCase());
}
System.out.println(s); // ここはコンパイルエラー
Java

s は「パターン変数」と呼ばれ、
if の条件が true になったときのブロック内でだけ有効です。

つまり、

  • if の中では sString として使える
  • 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

ここでは、

  1. まず obj instanceof String s が評価される
  2. true なら s が有効になり、s.length() > 3 が評価される

という順番になります。

&& の左側が true になったときだけ右側が評価される、という Java のルールのおかげで、
右側では安全に s を使えます。

||(OR)と組み合わせるときの注意

if (obj instanceof String s || s == null) {
    // ここで s を使おうとすると危険
}
Java

これは危ない書き方です。

|| の場合、左側が false のときに右側が評価されます。
つまり、obj instanceof String sfalse なら、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");
    }
}
Java

Circle のときは cRectangle のときは 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 の書き方。」

タイトルとURLをコピーしました