instanceofによるパターン・マッチング
instanceof
によるパターン・マッチングでは、ターゲットの型がパターンの型と一致するかどうかをテストします。一致する場合、ターゲットはパターンの型に変換され、パターン変数はターゲットのデータで自動的に初期化されます。
instanceof
演算子のパターン・マッチングに関する背景情報は、JEP 394を参照してください。
特定の形状の周辺の長さを計算する次のコードを見ていきます:
public interface Shape {
public static double getPerimeter(Shape s) throws IllegalArgumentException {
if (s instanceof Rectangle) {
Rectangle r = (Rectangle) s;
return 2 * r.length() + 2 * r.width();
} else if (s instanceof Circle) {
Circle c = (Circle) s;
return 2 * c.radius() * Math.PI;
} else {
throw new IllegalArgumentException("Unrecognized shape");
}
}
}
public class Rectangle implements Shape {
final double length;
final double width;
public Rectangle(double length, double width) {
this.length = length;
this.width = width;
}
double length() { return length; }
double width() { return width; }
}
public class Circle implements Shape {
final double radius;
public Circle(double radius) {
this.radius = radius;
}
double radius() { return radius; }
}
メソッドgetPerimeter
は、次を実行します:
Shape
オブジェクトのタイプを判断するためのテストinstanceof
演算子の結果に応じて、Shape
オブジェクトをRectangle
またはCircle
にキャストする変換Shape
オブジェクトから長さと幅または半径を抽出することによる分解
パターン・マッチングを使用すると、instanceof
演算子の2番目のオペランドがタイプ・パターンで変更され、変換のステップがなくなるため、短くて読みやすいコードになります:
public static double getPerimeter(Shape shape) throws IllegalArgumentException {
if (s instanceof Rectangle r) {
return 2 * r.length() + 2 * r.width();
} else if (s instanceof Circle c) {
return 2 * c.radius() * Math.PI;
} else {
throw new IllegalArgumentException("Unrecognized shape");
}
}
ノート:
この変換のステップがなくなることで、コードもより安全になります。instanceof
でオブジェクトのタイプをテストし、キャストを使用してそのオブジェクトを新しい変数に割り当てると、アプリケーションでコーディング・エラーが発生することがあります。あるオブジェクト(テスト対象のオブジェクトまたは新しい変数のいずれか)のタイプを変更し、その他のオブジェクトのタイプの変更を忘れてしまう場合があります。詳細は、「instanceofおよびswitchによる安全なキャスト」を参照してください。
この例では、2つのパターンを使用します:
Rectangle r
Circle c
パターンRectangle r
は、instanceof
式のオペランドです。ターゲットs
の型がパターンで指定したもの(Rectangle
)かどうかをテストします。同様に、式s instanceof Circle c
は、s
の型がCircle
かどうかをテストします。
値がパターンに正常に一致した場合、パターン変数はターゲットのデータで初期化されます。この例では、ターゲットs
がRectangle
の場合、s
はRectangle
に変換され、r
に割り当てられます。同様に、s
がCircle
の場合はCircle
に変換され、c
に割り当てられます。
パターン変数のスコープとinstanceof
instanceof
演算子がtrue
の場合にのみプログラムが到達できる箇所です:
public static double getPerimeter(Shape shape) throws IllegalArgumentException {
if (shape instanceof Rectangle s) {
// You can use the pattern variable s (of type Rectangle) here.
} else if (shape instanceof Circle s) {
// You can use the pattern variable s of type Circle here
// but not the pattern variable s of type Rectangle.
} else {
// You cannot use either pattern variable here.
}
}
パターン変数のスコープは、それを導入した文を超えて拡張できます:
public static boolean bigEnoughRect(Shape s) {
if (!(s instanceof Rectangle r)) {
// You cannot use the pattern variable r here because
// the predicate s instanceof Rectangle is false.
return false;
}
// You can use r here.
return r.length() > 5;
}
パターン変数は、if
文の式で使用できます:
if (shape instanceof Rectangle r && r.length() > 5) {
// ...
}
条件付きAND演算子(&&
)は短絡演算子であるため、プログラムがr.length() > 5
式に到達できるのは、instanceof
演算子がtrue
の場合のみです。
逆に、次のような場合には、instanceof
演算子でパターン・マッチングを実行することはできません。
if (shape instanceof Rectangle r || r.length() > 0) { // error
// ...
}
instanceof
がfalseの場合、プログラムはr.length() || 5
に到達できるため、ここではパターン変数r
は使用できません。
パターン変数を使用できるその他の例は、「パターン変数のスコープとswitch」を参照してください。