型パターン

型パターンは、型と1つのパターン変数で構成されます。値がパターンに出現する型のインスタンスであるかどうかをテストするために使用されます。

参照型を持つ型パターン

型パターンのパターン変数は、任意の参照型にできます。

次のswitch文は、セレクタ式objを、クラス型、列挙型、レコード型およびint[]配列型を含む型パターンと一致させます:

record Point(int x, int y) { }
enum Color { RED, GREEN, BLUE; }
//...

static void typeTester(Object obj) {
    switch (obj) {
        case null     -> System.out.println("null");
        case String s -> System.out.println("String");
        case Color c  -> System.out.println("Color with " + c.values().length + " values");
        case Point p  -> System.out.println("Record class: " + p.toString());
        case int[] ia -> System.out.println("Array of int values of length" + ia.length);
        default       -> System.out.println("Something else");
    }
}

プリミティブ型を持つ型パターン

参照型のかわりにプリミティブ型を型パターンに指定できます。

ノート:

型パターンでのプリミティブ型はプレビュー機能です。プレビュー機能は、設計、仕様および実装が完了したが、永続的でない機能です。プレビュー機能は、将来のJava SEリリースで、異なる形式で存在することもあれば、まったく存在しないこともあります。プレビュー機能が含まれているコードをコンパイルして実行するには、追加のコマンド行オプションを指定する必要があります。『Preview Language and VM Features』を参照してください。

詳細は、JEP 455: パターン、instanceofおよびswitchでのプリミティブ型(プレビュー)を参照してください。

次の例では、型パターンを使用して、値がfloatdoubleまたはintであるかどうかをテストします:

float v = 1000.01f;
        
if (v instanceof float f) System.out.println("float value: " + f);
if (v instanceof double d) System.out.println("double value: " + d);
if (v instanceof int i) System.out.println("int value: " + i);

次のような出力が表示されます:

float value: 1000.01
double value: 1000.010009765625

この例で変換されたdouble値が元のfloat値と異なる理由については、DoubleのJavaDoc APIドキュメントの10進数と2進数の間の変換の問題の項を参照してください。

ヒント:

値のキャスト時に型の安全性を確保するには、プリミティブによるパターン・マッチングを使用するのが特に有効です。詳細は、「instanceofおよびswitchによる安全なキャスト」を参照してください。

次のswitch式のcaseラベルは、floatを含む型パターンを使用しています:

String floatToRating(float rating) {
    return switch(rating) {
        case 0f      -> "0 stars";
        case 2.5f    -> "Average";
        case 5f      -> "Best";
        case float f -> "Invalid rating: " + f;
    };
}

パラメータ化された型を持つ型パターン

「instanceofおよびswitchによる安全なキャスト」で説明されているように、instanceof演算子を使用して、右オペランドの型を左オペランドの型にキャストできるかどうかをテストできます。これは、右オペランドが1つ以上のパラメータ化された型を含む型パターンである場合にも当てはまります。ただし、型消去(Javaチュートリアルの型消去を参照)のため、パラメータ化された型の情報は実行時に存在しません。したがって、ランタイムがパラメータ化された型を含む型間のキャストを完全に検証することはできません。コンパイラは、このような状況に対して警告またはエラーを生成します。ただし、パラメータ化された型を含むinstanceofオペランド間のキャストを完全に検証できない場合、コンパイラはエラーを生成します。

ListList<String>にキャストしようとする次の例について考えてみます:

List myList = null;
List<String> stringList = (List<String>) myList;

-Xlint:uncheckedオプションが指定されていると、コンパイラは次の警告を生成します:

warning: [unchecked] unchecked cast
List<String> stringList = (List<String>) myList;
                                          ^
  required: List<String>
  found:    List

instanceof演算子によって、myList (List)の型がList<String>かどうかをテストする次の例について考えてみます:

List myList = null;
        
if (myList instanceof List<String> sl) {
    System.out.println("myList is a List<String>, size " + sl.size());
}

コンパイラは、2番目のinstanceof式に対してエラーを生成します:

error: List cannot be safely cast to List<String>
if (myList instanceof List<String> sl) {
    ^

次に、パラメータ化された型を持つ型パターンを含むinstanceof式の例をさらに示します。

public class Box<T> {
    private T t;    
    public Box() { }
    public Box(T t) { this.t = t; }
    public void set(T t) { this.t = t; }
    public T get() { return t; }
}

public class Shoebox<T> extends Box<T> {    
    public Shoebox(T t) { super(t); }
}
// ...

Shoebox<String> sb = new Shoebox<>("a pair of new shoes");

if (sb instanceof Box<String> s) {
    System.out.println("Box<String> contains: " + s.get());
}
        
if (sb instanceof Shoebox<String> s) {
    System.out.println("Shoebox<String> contains: " + s.get());
}
        
if (sb instanceof Shoebox<?> s) {
    System.out.println("Shoebox<?> contains: " + s.get());
}
        
if (sb instanceof Shoebox<Object> s) {
    // error: incompatible types: Shoebox<String> cannot be
    //        converted to Shoebox<Object>
    System.out.println("Shoebox<Object> contains: " + s.get());
}

次の式はtrueを返します。instanceofパターン一致演算子の両側の型パラメータは同じです:

  • sb instanceof Box<String> s
  • sb instanceof Shoebox<String> s

sb instanceof Shoebox<?> sは、trueを返します。ワイルドカード型(?)は、すべての型に一致します。

ただし、StringObjectのサブタイプであっても、コンパイラがShoebox<Object>Shoebox<String>にキャストできるかどうかを実行時に完全に検証することはできません。型消去のため、このパラメータ化された型の情報は実行時に存在しません。その結果、コンパイラは次の式に対してエラーを生成します:

sb instanceof Shoebox<Object> s

ノート:

前の例では、instanceofをパターン一致演算子として使用しています。instanceofを型比較演算子として使用しても、パラメータ化された型と型消去に関する同じ問題が依然として当てはまります。たとえば、コンパイラは次の式に対してエラーを生成します:
sb instanceof Shoebox<Object>