4 パターン・マッチング
パターン・マッチングでは、オブジェクトに特定の構造があるかどうかをテストし、一致がある場合には、そのオブジェクトからデータを抽出します。これはJavaでも実行できることですが、パターン・マッチングには新しい言語拡張が導入されており、より簡潔で強力なコードを使用してオブジェクトから条件付きでデータを抽出できます。
instanceof演算子のパターン・マッチング
次の例では、パラメータshape
がRectangle
またはCircle
である場合のみ、その周辺の長さを計算します:
interface Shape { }
record Rectangle(double length, double width) implements Shape { }
record Circle(double radius) implements Shape { }
...
public static double getPerimeter(Shape shape) throws IllegalArgumentException {
if (shape instanceof Rectangle r) {
return 2 * r.length() + 2 * r.width();
} else if (shape instanceof Circle c) {
return 2 * c.radius() * Math.PI;
} else {
throw new IllegalArgumentException("Unrecognized shape");
}
}
パターンは、述語と呼ばれるテスト、ターゲット、およびパターン変数と呼ばれる一連のローカル変数の組合せです:
- 述語は、ある引数のブール値を持つ関数です。この場合は、
Shape
引数がRectangle
であるかCircle
であるかをテストするinstanceof
演算子が述語です。 - ターゲットは述語の引数で、
Shape
値がこれに当たります。 - パターン変数は、述語が
true
を返した場合のみ、ターゲットからのデータを格納する変数です。これらは、変数r
およびs
です。
詳細は、「instanceofのパターン・マッチング」を参照してください。
switch式および文のパターン・マッチング
ノート:
これは、設計、仕様および実装は完了しているが、永続的でないプレビュー機能であり、将来のJava SEリリースでは別の形式で存在するか、完全になくなる可能性があることを意味します。プレビュー機能が含まれているコードをコンパイルして実行するには、追加のコマンド行オプションを指定する必要があります。『Preview Language and VM Features』を参照してください。
switch
式および文のパターン・マッチングに関する背景情報は、JEP 420を参照してください。
次の例では、Rectangle
またはCircle
のインスタンスに対してのみ、周辺の長さを計算します。ただし、if-then-else
文のかわりにswitch
式が使用されます:
public static double getPerimeter(Shape shape) throws IllegalArgumentException {
return switch (shape) {
case Rectangle r -> 2 * r.length() + 2 * r.width();
case Circle c -> 2 * c.radius() * Math.PI;
default -> throw new IllegalArgumentException("Unrecognized shape");
}
}
「switch式および文のパターン・マッチング」を参照してください
ガード付きパターン
ノート:
この機能は、プレビュー機能であるJEP 420の一部です。ガード付きパターンでは、ブール式でパターンを絞り込むことができます。値がガード付きパターンに一致するのは、値がパターンに一致し、かつ、ブール式がtrueと評価される場合です。次に例を示します。
static void test(Object obj) {
switch (obj) {
case String s:
if (s.length() == 1) {
System.out.println("Short: " + s);
} else {
System.out.println(s);
}
break;
default:
System.out.println("Not a string");
}
}
ブール式s.length == 1
は、ガード付きパターンを使用してcase
ラベルに移動できます:
static void test(Object obj) {
switch (obj) {
case String s && (s.length() == 1) -> System.out.println("Short: " + s);
case String s -> System.out.println(s);
default -> System.out.println("Not a string");
}
}
最初のパターンは、obj
がString
で長さが1の場合に一致します。2番目のパターンは、obj
が異なる長さのString
である場合に一致します。
ガード付きパターンの形式は、p && e
で、pはパターン、e
はブール式です。pに表示されるパターン変数のスコープには、eが含まれます。これにより、長さが1より大きいStringにキャストできる値に一致するString s && (s.length() > 1)
などのパターンを指定できます。
カッコ付きパターン
ノート:
この機能は、プレビュー機能であるJEP 420の一部です。カッコ付きパターンは、カッコで囲まれたパターンです。ガード付きパターンはパターンと式を組み合せるため、解析のあいまいさが生じる可能性があります。パターンをカッコで囲むことで、これらのあいまいさを回避したり、パターンを含む式を異なる方法で解析するようにコンパイラに強制したり、コードの可読性を高めることができます。次に例を示します。
static Function<Integer, String> testParen(Object obj) {
boolean b = true;
return switch (obj) {
case String s && b -> t -> s;
default -> t -> "Default string";
};
}
この例はコンパイルされます。ただし、最初の矢印トークン(->
)がcase
ラベルの一部であり、ラムダ式の一部ではないことを明確にしたい場合は、ガード付きパターンをカッコで囲むことができます:
static Function<Integer, String> testParen(Object obj) {
boolean b = true;
return switch (obj) {
case (String s && b) -> t -> s;
default -> t -> "Default string";
};
}