Java Pattern Matching
Java 21で完成したパターンマッチングを学びます。
Pattern Matching とは
値の構造を検査し、構成要素を抽出する機能。
// 従来
if (obj instanceof String) {
String s = (String) obj;
System.out.println(s.length());
}
// Pattern Matching
if (obj instanceof String s) {
System.out.println(s.length()); // キャスト不要
}
instanceof パターン
基本
Object obj = "Hello";
if (obj instanceof String s) {
// s は String として使える
System.out.println(s.toUpperCase());
}
// 否定
if (!(obj instanceof String s)) {
return;
}
// ここでは s が使える(フロースコーピング)
System.out.println(s.length());
条件の組み合わせ
// AND
if (obj instanceof String s && s.length() > 5) {
System.out.println("Long string: " + s);
}
// OR は使えない(スコープが曖昧になるため)
// if (obj instanceof String s || obj instanceof Integer i) { } // コンパイルエラー
null の扱い
Object obj = null;
// instanceof は null に対して false
if (obj instanceof String s) {
// null の場合はここに入らない
}
switch 式のパターンマッチング
基本
Object obj = ...;
String result = switch (obj) {
case Integer i -> "Integer: " + i;
case Long l -> "Long: " + l;
case Double d -> "Double: " + d;
case String s -> "String: " + s;
case null -> "null";
default -> "Unknown: " + obj.getClass();
};
null の処理
// null を明示的に処理
String result = switch (obj) {
case null -> "It's null";
case String s -> "String: " + s;
default -> "Other";
};
// null と default をまとめる
String result = switch (obj) {
case String s -> "String: " + s;
case null, default -> "Not a string";
};
ガード条件(when)
String describe(Object obj) {
return switch (obj) {
case Integer i when i > 0 -> "Positive integer: " + i;
case Integer i when i < 0 -> "Negative integer: " + i;
case Integer i -> "Zero";
case String s when s.isEmpty() -> "Empty string";
case String s -> "String: " + s;
default -> "Other";
};
}
Record Pattern
Record の構成要素を分解して抽出。
基本
record Point(int x, int y) {}
Object obj = new Point(10, 20);
if (obj instanceof Point(int x, int y)) {
System.out.println("x=" + x + ", y=" + y);
}
// switch での使用
String describe(Object obj) {
return switch (obj) {
case Point(int x, int y) -> "Point at (" + x + ", " + y + ")";
default -> "Not a point";
};
}
var の使用
if (obj instanceof Point(var x, var y)) {
// x, y の型は推論される
}
ネストした Record Pattern
record Point(int x, int y) {}
record Line(Point start, Point end) {}
Object obj = new Line(new Point(0, 0), new Point(10, 10));
if (obj instanceof Line(Point(var x1, var y1), Point(var x2, var y2))) {
double length = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
System.out.println("Length: " + length);
}
// switch での使用
String describe(Object obj) {
return switch (obj) {
case Line(Point(var x1, var y1), Point(var x2, var y2))
when x1 == x2 -> "Vertical line";
case Line(Point(var x1, var y1), Point(var x2, var y2))
when y1 == y2 -> "Horizontal line";
case Line l -> "Diagonal line";
default -> "Not a line";
};
}