【プログラミングしてみよう40】JavaのPattern Matching入門(instanceofの進化)

Java

前回はSealed Classesを使って継承関係を明確に制限する方法を学びました。

今回は「Pattern Matching(パターンマッチング)」について解説します。 Java 16から正式導入されたPattern Matchingを使うことで、型チェックと型変換をまとめて書けるようになり、コードがより簡潔になります。

Pattern Matchingとは

これまで instanceof を使って型チェックをするとき、型チェックの後にキャストが必要でした。

// 従来の書き方
Object obj = "ぱんまる";
if (obj instanceof String) {
    String str = (String) obj; // キャストが必要
    System.out.println(str.length());
}

Java 16から導入されたPattern Matchingを使うと、型チェックと変数への代入を1行でまとめて書けます。

// Pattern Matchingを使った書き方
Object obj = "ぱんまる";
if (obj instanceof String str) {
    System.out.println(str.length()); // キャスト不要
}

さらにJava 21からは switch 文でもPattern Matchingが使えるようになり、型によって処理を分岐させる際に非常にシンプルに書けます。

サンプルコード(instanceofのPattern Matching)

ファイル作成

「Java28」フォルダに PatternMatchTest.java という名前でファイルを作成します

コード入力

以下のコードを入力します。

import java.util.List;

public class PatternMatchTest {
    public static void main(String[] args) {
        List<Object> items = List.of(
            "ぱんまる",
            42,
            3.14,
            true,
            List.of("a", "b", "c")
        );

        for (var item : items) {
            describe(item);
        }
    }

    static void describe(Object obj) {
        // 従来の書き方
        // if (obj instanceof String) {
        //     String str = (String) obj;
        //     System.out.println("文字列: " + str + " (長さ: " + str.length() + ")");
        // }

        // Pattern Matchingを使った書き方
        if (obj instanceof String str) {
            System.out.println("文字列: " + str + " (長さ: " + str.length() + ")");
        } else if (obj instanceof Integer num) {
            System.out.println("整数: " + num + " (2倍: " + (num * 2) + ")");
        } else if (obj instanceof Double dbl) {
            System.out.println("小数: " + dbl);
        } else if (obj instanceof Boolean bool) {
            System.out.println("真偽値: " + bool);
        } else if (obj instanceof List<?> list) {
            System.out.println("リスト: " + list + " (要素数: " + list.size() + ")");
        } else {
            System.out.println("不明な型: " + obj);
        }
    }
}

コンパイル

javac PatternMatchTest.java

実行

java PatternMatchTest

実行結果は次のようになります。

文字列: ぱんまる (長さ: 4)
整数: 42 (2倍: 84)
小数: 3.14
真偽値: true
リスト: [a, b, c] (要素数: 3)

サンプルコード(switch文のPattern Matching)

Java 21からは switch 文でもPattern Matchingが使えます。前回学んだSealed Classesと組み合わせると特に効果的です。

ファイル作成

「Java28」フォルダに SwitchPatternTest.java という名前でファイルを作成します

コード入力

以下のコードを入力します。

import java.util.List;

public class SwitchPatternTest {
    sealed interface Shape permits SwitchPatternTest.Circle,
                                   SwitchPatternTest.Rectangle,
                                   SwitchPatternTest.Triangle {}
    record Circle(double radius) implements Shape {}
    record Rectangle(double width, double height) implements Shape {}
    record Triangle(double base, double height) implements Shape {}

    static double calcArea(Shape shape) {
        return switch (shape) {
            case Circle c    -> Math.PI * c.radius() * c.radius();
            case Rectangle r -> r.width() * r.height();
            case Triangle t  -> t.base() * t.height() / 2;
        };
    }

    static String describe(Object obj) {
        return switch (obj) {
            case Integer i when i < 0  -> "負の整数: " + i;
            case Integer i             -> "正の整数: " + i;
            case String s when s.isEmpty() -> "空の文字列";
            case String s              -> "文字列: " + s;
            case null                  -> "null";
            default                    -> "その他: " + obj;
        };
    }

    public static void main(String[] args) {
        var shapes = List.of(
            new Circle(5),
            new Rectangle(4, 6),
            new Triangle(3, 8)
        );

        System.out.println("--- 図形の面積 ---");
        for (var shape : shapes) {
            System.out.printf("%s: %.2f%n", shape, calcArea(shape));
        }

        System.out.println("\n--- 型による分岐 ---");
        var items = new Object[]{42, -5, "ぱんまる", "", null, 3.14};
        for (var item : items) {
            System.out.println(describe(item));
        }
    }
}

コンパイル

javac SwitchPatternTest.java

実行

java SwitchPatternTest

実行結果は次のようになります。

--- 図形の面積 ---
Circle[radius=5.0]: 78.54
Rectangle[width=4.0, height=6.0]: 24.00
Triangle[base=3.0, height=8.0]: 12.00

--- 型による分岐 ---
正の整数: 42
負の整数: -5
文字列: ぱんまる
空の文字列
null
その他: 3.14

コードの解説

if (obj instanceof String str) はJava 16からの書き方で、objString であれば str という変数に代入して、そのまま if ブロック内で使えます。従来は型チェック後にキャストが必要でしたが、これにより1行にまとまります。

switch 文のPattern Matchingでは、case Circle c -> のように型と変数をまとめて書けます。Sealed Classesを使った Shape の例では、CircleRectangleTriangle の3つがすべて case で網羅されているため、default を書かなくてもコンパイルが通ります。これがSealed ClassesとPattern Matchingを組み合わせる大きなメリットです。

case Integer i when i < 0 ->when は「ガード条件」と呼ばれ、型チェックに追加の条件を組み合わせて書けます。when 句を使うことで「負の整数の場合」「空の文字列の場合」といった細かい条件分岐を、switch の中だけで完結して書けます。

case null -> でnullも switch の中で処理できます。従来の switch ではnullを渡すと NullPointerException が発生しましたが、Pattern Matchingの switch ではnullも安全に扱えます。

まとめ

Pattern Matchingを使うことで、型チェックとキャストをまとめて書けるようになり、コードがよりシンプルになります。instanceof のPattern Matchingはまず覚えておきたい基本で、switch のPattern Matchingは型によって処理を分岐させる場面で特に力を発揮します。Sealed Classesと組み合わせると、取りうる型をすべて case で網羅したことをコンパイラが確認してくれるため、書き漏らしのない安全な設計が実現できます。Java 21以降の機能を使う場合は、Javaのバージョンを確認してから活用しましょう。

2026/07/19(日) ぱんまる

コメント

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