CodeGym /コース /JAVA 25 SELF /Pattern Matching for instanceof

Pattern Matching for instanceof

JAVA 25 SELF
レベル 65, レッスン 1
使用可能

1. 従来の instanceof の問題

少しノスタルジーから始めましょう。Java 16 以前は、オブジェクトの型を確認してからその型として使うには、instanceof のチェックと明示的キャストを組み合わせた冗長なパターンを書く必要がありました。

Object obj = ...; // 何らかのオブジェクト

if (obj instanceof String) {
    String s = (String) obj;
    System.out.println("文字列の長さ: " + s.length());
}

退屈だが頻出のパターン

このパターンは非常に頻繁に登場します — イベントハンドラ、異種コレクションの処理、継承を伴うビジネスロジックなど。

なぜ不便なのか

  • コードの重複 — instanceof とキャストの両方で型を二重に書く必要があります。
  • エラーのリスク — 誤った型にキャストして ClassCastException を引き起こしやすい。
  • ノイズの多いコード — 複数のチェックがあると特に可読性が下がります。

2. Pattern Matching for instanceof: 新しい構文

Java 16 では instanceof にパターンマッチングが追加されました。これにより、型をチェックすると同時に、その型の変数を宣言し、if ブロック内で利用できます。

新しい構文

if (obj instanceof String s) {
    // s はすでに文字列です!
    System.out.println("文字列の長さ: " + s.length());
}
  • instanceof の型の後ろに新しい変数名(String s)を書きます。
  • 変数は条件が真のブロック内でのみ有効です。
  • もう (String) のキャストを書く必要はありません — コンパイラが保証します。

どこが「魔法」? コンパイラが型をチェックし、合致すればその型の変数を「展開」します。そうでなければブロックは実行されません。

実際のコードではこう見える

Object obj = "こんにちは、Java 16+!";

if (obj instanceof String s) {
    System.out.println("大文字: " + s.toUpperCase());
} else {
    System.out.println("これは文字列ではありません!");
}

3. 安全性と可読性: これが「良い」と言える理由

ClassCastException を排除

Object obj = 123;
// String s = (String) obj; // ドカン!例外発生。

パターンマッチングでは、型チェックが成功した場合にのみ変数が作られるので、「誤った」キャストそのものが存在しません。

コードが短く、よりわかりやすくなる
2 行が 1 行になります。ノイズが減るほど読みやすく、保守もしやすくなります。

例: 複数の型を処理する

public static void printInfo(Object obj) {
    if (obj instanceof String s) {
        System.out.println("文字列の長さ: " + s.length());
    } else if (obj instanceof Integer i) {
        System.out.println("整数: " + (i + 1));
    } else {
        System.out.println("不明な型: " + obj);
    }
}

4. 使用例と細かな注意点

複数の型を順にチェック

Object value = ...;

if (value instanceof String s) {
    System.out.println("これは文字列です: " + s);
} else if (value instanceof Number n) {
    System.out.println("これは数値です: " + n);
} else {
    System.out.println("その他: " + value);
}

null と一緒に使う
オブジェクトが null の場合、instanceof は常に false を返します — NPE は起きませんが、ブロックは実行されません。

Object obj = null;

if (obj instanceof String s) {
    // obj == null の場合、このブロックは決して実行されません
    System.out.println("文字列: " + s);
} else {
    System.out.println("obj は null か、文字列ではありません");
}

継承とパターンマッチング

class Animal {}
class Dog extends Animal {
    void bark() { System.out.println("ワン!"); }
}

Animal a = new Dog();

if (a instanceof Dog d) {
    d.bark(); // キャストなしで Dog のメソッドを呼べます!
}

確認の順序は重要です。より具体的な型からより一般的な型へ進めてください。そうしないと「狭い」判定が届かないことがあります。

比較: 従来と新しいアプローチ

従来の方法 新しい方法(pattern matching)
if (obj instanceof Cat) {
    Cat c = (Cat) obj;
    c.meow();
}
if (obj instanceof Cat c) {
    c.meow();
}

5. instanceof に対するパターンマッチングの制約

  • 変数のスコープ。 パターンで導入された変数は、判定が真のブロック内でのみ有効です。
if (obj instanceof String s) {
    System.out.println(s); // s は参照可能
}
// System.out.println(s); // エラー!ここでは s は見えません
  • 1 つの条件式内で異なる型の変数を複数宣言することはできません。
// コンパイルエラー:
if (obj instanceof String s || obj instanceof Integer i) {
    // ...
}
  • Java 16+ が必要。 より古い JDK バージョンでは新しい構文はサポートされません。

6. 実用例(簡単なアプリをベースに)

異なるタイプのタスクを持つ簡単な Task Manager を想定します。

クラス

class Task {
    String title;
    public Task(String title) { this.title = title; }
}

class BugTask extends Task {
    int severity;
    public BugTask(String title, int severity) {
        super(title);
        this.severity = severity;
    }
}

class FeatureTask extends Task {
    String feature;
    public FeatureTask(String title, String feature) {
        super(title);
        this.feature = feature;
    }
}

pattern matching を使った処理

public static void processTask(Task t) {
    if (t instanceof BugTask bug) {
        System.out.println("バグ: " + bug.title + ", 重大度: " + bug.severity);
    } else if (t instanceof FeatureTask feature) {
        System.out.println("機能: " + feature.title + ", モジュール: " + feature.feature);
    } else if (t instanceof Task task) {
        System.out.println("通常のタスク: " + task.title);
    }
}

7. 役立つニュアンス

表: instanceof に対する Pattern Matching — 長所と短所

長所 短所/制限
コード量が減り、可読性が向上 Java 16+ が必要
ClassCastException のリスクがない 変数は if ブロック内でのみ可視
コンパイル時の型安全性 1 つの条件で複数型には対応しない
継承の処理に便利 古い IDE やビルドでは未対応の可能性
任意のクラスで利用可能

ビジュアル図解: instanceof の pattern matching はどう動くか

+---------------------------+
|      Object obj           |
+---------------------------+
            |
            v
  if (obj instanceof Type t)
            |
        はい        いいえ
      (true)       (false)
        |             |
        v             v
 t は利用可能     t は存在しない
      (コード)         (アクセス不可)

8. instanceof の pattern matching でありがちな誤り

エラー1: if ブロックの外で変数を使おうとする。 パターンで変数を宣言しても、ブロックを抜ければその変数は消えます。コンパイラはもっともで、「s はどこにあるの?」と言って怒ります。

エラー2: instanceof が null でも真になると期待する。 オブジェクトが null の場合、instanceof は常に偽で、変数は作られません。必要なら null を別途処理しましょう。

エラー3: 古い JDK/IDE で新しい構文を使う。 Java 11 以下では instanceof のパターンマッチングは利用できず、構文エラーになります。JDK のバージョンを確認してください。

エラー4: スコープの勘違い。 パターンで導入された変数は、チェックが真の分岐内でのみ有効です。範囲外では存在しません。

エラー5: 一度に複数の型に対応できると期待する。 1 つの条件で異なる型の変数を同時に宣言することはできません: if (obj instanceof String s || obj instanceof Integer i) — これは不可。型ごとに個別のチェックを書いてください。

1
タスク
JAVA 25 SELF, レベル 65, レッスン 1
ロック未解除
インテリジェントデータアナライザー 🔍
インテリジェントデータアナライザー 🔍
1
タスク
JAVA 25 SELF, レベル 65, レッスン 1
ロック未解除
シェルターでのペット識別 🐾
シェルターでのペット識別 🐾
コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION