BT

最新技術を追い求めるデベロッパのための情報コミュニティ

寄稿

Topics

地域を選ぶ

InfoQ ホームページ アーティクル Javaの新機能総合ガイド: スイッチのパターンマッチング

Javaの新機能総合ガイド: スイッチのパターンマッチング

キーポイント

  • switchコントロール・フロー文のパターン・マッチは、Java 17で導入された新機能で、その後のバージョンで改良された

  • セレクタ式を評価し、パターンを含むケース・ラベルに対してその結果の値がテストされる。最初にマッチしたケース・ラベルのcase pがswitch文/式に適用される

  • パターン・マッチングでは、従来のレガシー型に加えて、任意の参照型のセレクタ式がサポートされる

  • ガードされたパターンは、ケース・ラベル・パターンの新しい when 節で使用可能

  • パターン・マッチングは、従来の switch 文と、従来の switch 文のフォールスルー・セマンティクスで使用できる

switch文は制御フロー文の1つで、もともとは、ある式がどのように評価されるかに基づいて複数の実行経路が考えられるような特定の使用例に対して、if-else if-else制御フロー文に代わる短い形式の制御フロー文として設計された。

switch文は、セレクタ式ケース・ラベルとケース・ラベルで構成されるswitchブロックで構成され、セレクタ式が評価され、どのケース・ラベルが評価結果と一致するかによって実行パスが切り替えられる。

もともとswitchは、フォールスルー・セマンティクスを持つ従来のcase …:: label構文を持つステートメントとしてのみ使用できた。Java 14では、フォールスルー・セマンティクスを持たない新しいcase …:-> label構文がサポートされたのである。

Java 14では、switch式のサポートも追加された。switch式は1つの値に対して評価される。yield文は、明示的に値を返すために導入された。

別の記事で詳しく説明するが、switch式のサポートは、代入文のような式を要求するインスタンスでswitchを使用できることを意味する。

問題点

しかし、Java 14で強化されたとはいえ、switchにはまだいくつかの制限がある。

  1. switchのセレクタ式は、特定の型、すなわちプリミティブ型(基本型)byteshortcharint、対応する参照型(ラッパークラス)ByteShortCharacterIntegerStringクラス、列挙型しかサポートしていない

  2. スイッチ・セレクタ式の結果は、定数と完全に等しいかどうかだけをテストできる。ケース・ラベルと定数のマッチングは、1つの値に対してのみテストされる

  3. null値は他の値と同様に扱われれない

  4. エラー処理が統一されていない

  5. 列挙型の利用できる範囲が狭い

解決策

これらの制限に対抗するために、便利な解決策が提案され、実装されている。 それはswitch文と式のパターン・マッチングだ。この解決策は、上記のすべての問題に対処している。

switchのパターンマッチングはJDK 17で導入され、JDK 18、19、20で改良され、JDK 21で確定される予定だ。

パターン・マッチは、いくつかの点で従来の switchの制限を克服している。

  1. セレクタ式の型は、(longを除く)プリミティブ型(基本型)に加えて、任意の参照型にできる

  2. ケース・ラベルには、定数だけでなくパターンも含めることができる。パターン・ケース・ラベルは、1つの値にのみ適用される定数ケース・ラベルとは異なり、多くの値に適用できる。新しいケース・ラベルとして、pがパターンであるcase pが導入された

  3. ケース・ラベルにはnullを含めることが可能だ

  4. オプションのwhen句は、条件付きまたはガード付きパターン・マッチのために、ケース・ラベルの後に付けることができる。whenを持つケース・ラベルは、ガード付きケース・ラベルと呼ばれる

  5. 列挙定数ケース・ラベルは修飾できる。列挙定数を使用する場合、セレクタ式は列挙型である必要はない

  6. MatchException は、パターン・マッチにおけるエラー処理をより統一的にするために導入された

  7. 従来の switch 文と従来のフォールスルー・セマンティクスもパターン・マッチをサポートしている

パターン・マッチングの利点は、複雑なデータ指向クエリのパフォーマンスを向上させるなど、データ指向プログラミングを容易にする。

パターン・マッチとは?

パターン・マッチは、プログラミングにおけるコントロール・フロー構造の機能を拡張する強力な機能だ。この機能により、セレクタ式は、従来からサポートされている定数に対するテストに加えて、いくつかのパターンに対してもテスト可能になる。スイッチのセマンティクスに変更はない。値は、パターンを含むケース・ラベルに対してテストされ、セレクタ式の値がケース・ラベル・パターンにマッチすると、そのケース・ラベルがスイッチ制御フローの実行パスに適用されるのだ。唯一の強化点は、セレクタ式が、(longを除く)プリミティブ型(基本型)に加えて、任意の参照型にできる。ケース・ラベルには、定数だけでなくパターンも含めることが可能だ。さらに、nullやenum定数をケース・ラベルでサポートする機能が追加された。

スイッチ・ブロック内のスイッチ・ラベルの文法は以下の通りである。

SwitchLabel:
  case CaseConstant { , CaseConstant } 
  case null [, default]
  case Pattern
  default

パターン・マッチは、フォールスルー・セマンティクスを持つ従来の case …: ラベル構文と、フォールスルー・セマンティクスを持たない case … -> ラベル構文で使用できる。とはいえ、スイッチ・ブロックが2種類のケース・ラベルを混在できないことに注意する必要があるのだ。

これらの修正により、パターン・マッチはより洗練されたコントロール・フロー構造への道を開き、コードにおけるロジックへのアプローチがより豊かなものとなった。

環境の設定

この記事のコード・サンプルを実行するための唯一の前提条件は、Java 20またはJava 21(利用可能な場合)をインストールすることだ。Java 21では、Java 20と比較して、ケース・ラベルの修飾付き列挙定数のサポートが1つだけ強化されている。Javaのバージョンは、以下のコマンドで確認可能だ。

java --version
java version"20.0.1" 2023-04-18
Java(TM) SE Runtime Environment ( (build 20.0.1+9-29)
Java HotSpot(TM) 64-Bit Server VM (build 20.0.1+9-29、mixed mode,、sharing)

スイッチ・パターン・マッチはJava 20のプレビュー機能であるため、javacコマンドとjavaコマンドは以下の構文で実行する必要がある。

javac --enable-preview --release 20 SampleClass.java
java --enable-preview SampleClass

ただし、ソース・コード・ランチャーを使って直接実行もできる。その場合、コマンド・ラインは次のようになる。

java --source 20 --enable-preview Main.java

jshellオプションも利用できるが、プレビュー機能も有効にする必要がある。

jshell --enable-preview

パターン・マッチングの簡単な例

スイッチ式のセレクタ式タイプが参照型;collection;;であり、ケース・ラベルが case p という形式のパターンを含むパターンマッチングの簡単な例から始める。

import java.util.ollection;
import java.util.collection;
importjava.util.Stack;
importjava.util.Vector; 

public class SampleClass {
    static Object get(Collection c) {

        return switch (c) { 
            case Stack s -> s.pop();
            case LinkedList l -> l.getFirst();
            case Vector v -> v.lastElement();
            default -> c;
        };
    }

    public static void main(String[] argv) { 

        var stack = new Stack<String>(); 
        stack.push("firstStackItemAdded");
        stack.push("secondStackItemAdded"); 
        stack.push("thirdStackItemAdded");

        var linkedList = new LinkedList<String>(); 

        linkedList.add("firstLinkedListElementAdded");  
        linkedList.add("secondLinkedListElementAdded");  
        linkedList.add("thirdLinkedListElementAdded");

        var vector = new Vector<String>();

        vector.add("firstVectorElementAdded");
        vector.add("secondVectorElementAdded");
        vector.add("thirdVectorElementAdded");

        System.out.println(get(stack)); 
        System.out.println(get(linkedList)); 
        System.out.println(get(vector)); 
    }
}

Javaアプリケーションをコンパイルし、実行して出力する。

thirdStackItemAdded
firstLinkedListElementAdded
thirdVectorElementAdded

パターン・マッチはすべての参照型をサポートする

先に示した例では、Collection クラス型がセレクタ式型として使用されている。しかし、任意の参照型をセレクタ式型として使用可能だ。したがって、ケース・ラベル・パターンは、セレクタ式の値と互換性のある任意の参照型にできる。たとえば、次の変更後の SampleClass では、Object 型のセレクタ式が使用され、以前に使用した StackLinkedList、および Vector 参照型のケース・ラベル・パターンに加えて、レコード・パターンと配列参照型のケース・ラベル・パターンが含まれているのだ。

import java.util.LinkedList;
import java.util.Stack;
import java.util.Vector;

record CollectionType(Stack s, Vector v, LinkedList l) {
}

public class SampleClass {
    static Object get(Object c) {
        return switch (c) {
            case CollectionType r -> r.toString();
            case String[] arr -> arr.length;
            case Stack s -> s.pop();
            case LinkedList l -> l.getFirst();
            case Vector v -> v.lastElement();
            default -> c;
        };
    }

    public static void main(String[] argv) {

        var stack = new Stack<String>();
        stack.push("firstStackItemAdded");
        stack.push("secondStackItemAdded");
        stack.push("thirdStackItemAdded");

        var linkedList = new LinkedList<String>();

        linkedList.add("firstLinkedListElementAdded");
        linkedList.add("secondLinkedListElementAdded");
        linkedList.add("thirdLinkedListElementAdded");

        var vector = new Vector<String>();

        vector.add("firstVectorElementAdded");
        vector.add("secondVectorElementAdded");
        vector.add("thirdVectorElementAdded");

        var r = new CollectionType(stack, vector, linkedList);
        System.out.println(get(r));
        String[] stringArray = {"a", "b", "c"};

        System.out.println(get(stringArray));
        System.out.println(get(stack));
        System.out.println(get(linkedList));
        System.out.println(get(vector));

    }
}``

今回の出力は以下の通りである。

CollectionType[s=[firstStackItemAdded, secondStackItemAdded, thirdStackItemAdded
], v=[firstVectorElementAdded, secondVectorElementAdded, thirdVectorElementAdded
], l=[firstLinkedListElementAdded, secondLinkedListElementAdded, thirdLinkedList
ElementAdded]]
3
thirdStackItemAdded
firstLinkedListElementAdded
thirdVectorElementAdded

null・ケース・ラベル

従来、セレクタ式がnullと評価されると、switchは実行時にNullPointerExceptionに投げる。nullセレクタ式はコンパイル時の問題ではない。以下のマッチ・オール・ケース・ラベルのデフォルトを持つ単純なアプリケーションは、NULLセレクタ式が実行時にNullPointerExceptionに投げることを示している。

import java.util.Collection;

public class SampleClass {
    static Object get(Collection c) {
        return switch (c) {
            default -> c;
        };
    }

    public static void main(String[] argv) {
        get(null);
    }
}

switchブロックの外側で明示的にnull値をテストし、nullでない場合にのみswitchを起動することは可能だが、それにはif-elseコードを追加する必要がある。Javaは、新しいパターン・マッチ機能でcase nullをサポートするようになった。以下のアプリケーションのswitch文は、nullに対するセレクタ式をテストするためにcase nullを使用している。

import java.util.Collection;

public class SampleClass {
    static void get(Collection c) {

        switch (c) {
            case null -> System.out.println("Did you call the get with a null?");
            default -> System.out.println("default");
        }
    }

    public static void main(String[] argv) {
        get(null);
    }
}

実行時、アプリケーションはこう出力する。

Did you call the get with a null?

NULLのケースは、以下のようにデフォルトのケースと組み合わせることが可能だ。

import java.util.Collection;

public class SampleClass {
    static void get(Collection c) {
        switch (c) {
            case null, default -> System.out.println("Did you call the get with a null?");
        }
    }

    public static void main(String[] argv) {
        get(null);
    }
}

ただし、 ケースnullは他のケース・ラベルと組み合わせることはできない。例えば、以下のクラスは、ケースNULLとパターンStack s:を持つケース・ラベルを組み合わせている。

import java.util.Collection;
import java.util.Stack;

public class SampleClass {
    static void get(Collection c) {
        switch (c) {
            case null, Stack s -> System.out.println("Did you call the get with a null?");
            default -> System.out.println("default");
        }
    }

    public static void main(String[] args) {
        get(null);
    }
}

このクラスはコンパイル時にエラーを発生する。

SampleClass.java:11: error: invalid case label combination
          case null, Stack s -> System.out.println("Did you call the get with a null?");

when節によるガードパターン

開発者は、ブーリアン式の結果に基づいてマッチする条件付きケースラベルパターンを使うことがある。このようなときに when 節が役に立つのだ。この句はブーリアン式を評価し、いわゆる 'ガード付きパターン'を形成する。例えば、次のコード・スニペットの最初のケース・ラベルのwhen句は、Stackが空かどうかを判定するのだ。

import java.util.Stack;
import java.util.Collection;

public class SampleClass {
    static Object get(Collection c) {
        return switch (c) {
            case Stack s when s.empty() -> s.push("first");
            case Stack s2 -> s2.push("second");
            default -> c;
        };
    }
}

対応するコードは'->'の右側にあり、スタックが本当に空の場合にのみ実行される。

パターンによるケース・ラベルの順序は重要である

パターンを含むケース・ラベルを使用する場合、開発者は型やサブタイプの階層に関連する問題が生じないような順序にしなければならない。定数のケース・ラベルとは異なり、ケース・ラベルのパターンでは、セレクタ式がパターンを含む複数のケース・ラベルに対応できるからだ。スイッチのパターンマッチング機能は、パターンがセレクタ式の値と一致する最初のケースラベルにマッチする。

ケース・ラベル・パターンの型が、その前に現れる別のケース・ラベル・パターンの型のサブタイプである場合、後者のケース・ラベルは到達不能コードとして識別されるため、コンパイル時エラーが発生する。

このシナリオを実証するために、開発者は、Object 型のケース・ラベル・パターンが Stack 型の後続のコード・ラベル・パターンに影響する以下のサンプル・クラスをコンパイルして実行できる。

import java.util.Stack;

public class SampleClass {
    static Object get(Object c) {
        return switch (c) {
            case Object o  -> c;
            case Stack s  -> s.pop();
        };
    }
}

クラスをコンパイルすると、エラー・メッセージが表示される。

SampleClass.java:12: error: this case label is dominated by a preceding case lab
el
        case Stack s  -> s.pop();
             ^

コンパイル時のエラーは、以下のように2つのケース・ラベルの順番を逆にするだけで修正可能だ。

public class SampleClass {
    static Object get(Object c) {
        return switch (c) {
            case Stack s  -> s.pop();
            case Object o  -> c;
        };
    }
}

同様に、ケース・ラベルが、無条件/ガードなしパターン(ガード付きパターンについては以前のセクションで説明した)を持つ先行するケース・ラベルと同じ参照型のパターンを含む場合、クラスと同じ理由でコンパイル型エラーになる。

import java.util.Stack;
import java.util.Collection;

public class SampleClass {
    static Object get(Collection c) {
        return switch (c) {
            case Stack s -> s.push("first");
            case Stack s2 -> s2.push("second");
        };
    }
}

コンパイル時に以下のエラーメッセージが表示される。

SampleClass.java:13: error: this case label is dominated by a preceding case lab
el
        case Stack s2 -> s2.push("second");

このようなエラーを避けるために、開発者はラベルケースの順序をわかりやすく、読みやすく保つべきである。定数ラベルを最初に列挙し、その後にケース・null・ラベル、ガード付きパターン・ラベル、ガードなしタイプ・パターン・ラベルの順にだ。defaultのケース・ラベルは、ケース・null・ラベルと組み合わせることも可能で、なおかつ最後のケース・ラベルとして個別に配置できる。以下のクラスは正しい順序を示している。

import java.util.Collection;
import java.util.Stack;
import java.util.Vector;

public class SampleClass {
    static Object get(Collection c) {
        return switch (c) {
            case null -> c;  //case label null
            case Stack s when s.empty() -> s.push("first");  // case label with guarded pattern
            case Vector v when v.size() > 2 -> v.lastElement();  // case label with guarded pattern
            case Stack s -> s.push("first");  // case label with unguarded pattern
            case Vector v -> v.firstElement();  // case label with unguarded pattern
            default -> c;
        };
    }
}

パターン・マッチは、従来のswitch文でも、フォールスルー・セマンティクスでも使用できる

パターン・マッチ機能は、switch文かswitch式かには関係ない。また、パターン・マッチは、case ...: ラベルを使用したフォールスルー・セマンティクスか、case ...-> ラベルを使用したノー・フォールスルー・セマンティクスかにも依存しない。以下の例では、パターン・マッチはswitch式ではなくswitch文で使用されている。ケース・ラベルは、case ...: ラベルによるフォールスルー・セマンティクスを使用し。最初の case …: ラベルの when 節は、ガードされたパターンを使用する。

import java.util.Stack;
import java.util.Collection;

public class SampleClass {
    static void get(Collection c) {
        switch (c) {
            case Stack s when s.empty(): s.push("first"); break;
            case Stack s : s.push("second");  break;
            default : break;
        }
    }


}

パターン変数の範囲

パターン変数とは、ケース・ラベル・パターンに現れる変数である。パターン変数のスコープは、->矢印の右側に表示されるブロック、式、またはthrow文に限定される。次のコード・スニペットでは、直前のケース・ラベルのパターン変数がデフォルトのケース・ラベルで使用されているのだ。

import java.util.Stack;

public class SampleClass {
    static Object get(Object c) {
        return switch (c) {
            case Stack s -> s.push("first");
            default -> s.push("first");
        };
    }
}

コンパイル時にエラーが発生する。

import java.util.Collection;
SampleClass.java:13: error: cannot find symbol
        default -> s.push("first");
                   ^
  symbol:   variable s
  location: class SampleClass

例で示したように、ガードされたケース・ラベルのパターンに現れるパターン変数のスコープには、when句が含まれる。

import java.util.Stack;
import java.util.Collection;

public class SampleClass {
    static Object get(Collection c) {
        return switch (c) {
            case Stack s when s.empty() -> s.push("first");
            case Stack s -> s.push("second");
            default -> c;
        };
    }
}

パターン変数のスコープが限られているため、同じパターン変数名を複数のケース・ラベルで使用できる。これは、パターン変数sが2つの異なるケース・ラベルで使用されている前述の例で示されている。

フォールスルー・セマンティクスを持つケース・ラベルを扱う場合、パターン変数のスコープは':'の右側に位置するステートメントのグループまで拡張される。そのため、従来のswitch文とパターン・マッチを使用することで、前節の2つのケース・ラベルに同じパターン変数名を使用できた。しかし、パターン変数を宣言するフォールスルー・ケースラベルはコンパイル時のエラーとなる。このことは、先ほどのクラスの次のバリエーションで示すことが可能だ。

import java.util.Stack;
import java.util.Vector;
import java.util.Collection;

public class SampleClass {
    static void get(Collection c) {
        switch (c) {
            case Stack s : s.push("second");
            case Vector v  : v.lastElement();
            default : System.out.println("default");
        }
    }
}

最初のステートメント・グループにbreak;ステートメントがないと、スイッチは2番目のステートメント・グループでパターン変数vを初期化することなく、2番目のステートメント・グループにフォールススルーする可能性がある。前のクラスはコンパイル・エラーを発生させる。

SampleClass.java:12: error: illegal fall-through to a pattern
        case Vector v  : v.lastElement();
       ^
        

最初のステートメント群に、以下のようにbreak;ステートメントを追加するだけで、エラーは修正される。

import java.util.Stack;
import java.util.Vector;
import java.util.Collection;

public class SampleClass {
    static void get(Collection c) {
        switch (c) {
            case Stack s : s.push("second"); break;
            case Vector v  : v.lastElement();
            default : System.out.println("default");
        }
    }
}

ケースラベルは1パターンのみ

case ...:、 型のケース・ラベルであろうと case ...-> 型のケース・ラベルであろうと、1つのケース・ラベル内で複数のパターンを組み合わせること許されず、コンパイル時のエラーとなる。わかりにくいかもしれないが、1つのケース・ラベルの中でパターンを組み合わせると、以下のクラスで示されているように、パターンのフォールスルーが発生する。

import java.util.Stack;
import java.util.Vector;
import java.util.Collection;

public class SampleClass {
    static Object get(Collection c) {
        return switch (c) {
            case Stack s, Vector v -> c;
            default -> c;
        };
    }
}

コンパイル時にエラーが発生する。

SampleClass.java:11: error: illegal fall-through from a pattern
        case Stack s, Vector v -> c;
                      ^

スイッチ・ブロック内のマッチ・オール・ケース・ラベルは1つだけ

switch文であれswitch式であれ、switchブロック内に複数のmatch-all caseラベルを持つことはコンパイル時のエラーである。match-allケース・ラベルは、

  1. セレクタ式に無条件にマッチするパターンを持つケース・ラベル

  2. デフォルトのケース・ラベル

次のクラスを考えてみよう。

import java.util.Collection;

public class SampleClass {
    static Object get(Collection c) {
        return switch (c) {
            case Collection coll -> c;
            default -> c;
        };
    }
}

クラスをコンパイルすると、エラー・メッセージが表示される。

SampleClass.java:13: error: switch has both an unconditional pattern and a default label
        default -> c;
        ^

タイプ・カバレッジの網羅性

網羅性は、スイッチ・ブロックがセレクタ式のすべての可能な値を処理しなければならないことを意味する。網羅性の要件は、以下の1つ以上に該当する場合にのみ実装される。

  • a) パターン・スイッチ式/ステートメントが使用されている
  •  
  • b) ケースNULLが使用される
  •  
  • c) セレクタ式がレガシー型(charbyteshortintCharacterByteShortIntegerString、enum型)のいずれかでない

網羅性を実装するためには、セレクタ式型のサブタイプが少ない場合、それぞれのサブタイプに対してケースラベルを追加すれば十分かもしれない。 例えば、Object型のセレクタ式に対して各参照型のケース・ラベルを追加したり、Collection型のセレクタ式に対して各サブ・タイプのケース・ラベルを追加することは現実的ではない。

網羅性の要件を示すために、次のクラスを考えてみよう。

import java.util.Collection;
import java.util.Stack;
import java.util.LinkedList;
import java.util.Vector;

public class SampleClass {
    static Object get(Collection c)   {
        return switch (c) {
            case Stack s  -> s.push("first");
            case null  -> throw new NullPointerException("null");
            case LinkedList l    -> l.getFirst();
            case Vector v  -> v.lastElement();
        };
    }
}  

このクラスはコンパイル時にエラー・メッセージを生成する。

SampleClass.java:10: error: the switch expression does not cover all possible in
put values
                return switch (c) {
                       ^

この問題は、以下のようにdefaultのケースを追加するだけで解決できる。

import java.util.Collection;
import java.util.Stack;
import java.util.LinkedList;
import java.util.Vector;

public class SampleClass {
    static Object get(Collection c)   {
        return switch (c) {
            case Stack s  -> s.push("first");
            case null  -> throw new NullPointerException("null");
            case LinkedList l    -> l.getFirst();
            case Vector v  -> v.lastElement();
            default -> c;
        };
    }
}  

次のクラスのような、セレクタ式に無条件にマッチするパターンを持つマッチオールケースラベルは、網羅的ではあるが、サブタイプを区別して扱ったり処理したりはしない。

import java.util.Collection;

public class SampleClass {
    static Object get(Collection c)   {
        return switch (c) {
            case Collection coll  -> c;
        };
    }
}  

defaultのケース・ラベルは、網羅性のために必要な場合もあるが、セレクタ式で取り得る値が非常に少ない場合は、回避できる場合もある。例として、セレクタ式がjava.util.Vector型の場合、defaultケースを避けるために、単一のサブクラスjava.util.Stackの1つだけ必要だ。同様に、セレクタ式がsealed class型の場合、sealed class型のpermit節で宣言されたクラスだけがswitchブロックで処理される必要がある。

スイッチ・ケース・ラベルのジェネリック・レコード・パターン

Java 20では、switch文/式のジェネリック・レコード・パターンに対する型引数の推論がサポートされた。例として、ジェネリック・レコードを考えてみよう。

record Triangle<S,T,V>(S firstCoordinate, T secondCoordinate,V thirdCoordinate){};

次のスイッチ・ブロックでは、推測されるレコード・パターンは次のようになる。

Triangle<Coordinate,Coordinate,Coordinate>(var f, var s, var t):
 
static void getPt(Triangle<Coordinate, Coordinate, Coordinate> tr){
        switch (tr) {
           case Triangle(var f, var s, var t) -> …;
           case default -> …;
        }
}

MatchExceptionによるエラー処理

Java 19では、パターン・マッチ時の例外処理をより統一的にするために、 java.lang.Runtimeクラスの新しいサブクラスが導入された。java.lang.MatchExceptionと呼ばれる新しいクラスは、プレビューAPIである。MatchExceptionは、スイッチでのパターン・マッチのために特別に設計されたものではなく、むしろあらゆるパターン・マッチ言語構文のために設計されたものだ。MatchExceptionは、網羅的なパターン・マッチが提供されたパターンのいずれにもマッチしなかった場合に、実行時に投げられることがある。このことを示すために、0による除算を持つアクセサ・メソッドを宣言するレコードのケース・ラベルにレコード・パターンを含む以下のアプリケーションを考えてみよう。

record DivisionByZero(int i) {
    public int i() {
        return i / 0;
    }
}


public class SampleClass {

    static DivisionByZero get(DivisionByZero r) {
        return switch(r) {
        case DivisionByZero(var i) -> r;
        };

    }

    public static void main(String[] argv) {

        get(new DivisionByZero(42));
    }
}

サンプル・アプリケーションはエラーなしでコンパイルされるが、実行時にMatchExceptionが投げられる。

Exception in thread "main" java.lang.MatchException: java.lang.ArithmeticException: / by zero
        at SampleClass.get(SampleClass.java:7)
        at SampleClass.main(SampleClass.java:14)
Caused by: java.lang.ArithmeticException: / by zero
        at DivisionByZero.i(SampleClass.java:1)
        at SampleClass.get(SampleClass.java:1)
        ... 1 more

結論

この記事では、switchコントロール・フロー構文の新しいパターン・マッチングについて紹介した。主な改良点は、switchのセレクタ式に任意の参照型を指定できるようになった点と、switchのケース・ラベルに条件付きパターン・マッチを含むパターンを指定可能になったことだ。また、コードベース全体を更新しなくても、従来のswitch文や従来のフォールスルー・セマンティクスでパターン・マッチがサポートされる。

作者について

この記事に星をつける

おすすめ度
スタイル

BT