JDK 21でswitch式でjava.lang.MatchExceptionを発生させるサンプル

めでたくJDK 21がリリースされたので多少試していたわけですが、switch式に色々と網羅性検査が入ったわけで、Scalaと比較してどうなるか?を試したメモ。

JavaでもScalaでのMatchErrorと同じような例外classが新たに用意されて、最悪それがthrowされるらしいです。 網羅してないとScalaと違ってJavaではコンパイルエラーとはいえ、頑張ればいくらでも原理上は発生させられるはずで、発生させるサンプルを作った、というだけの話。

発生させる方法も、他にもいくらでもありそうだけれど、とりあえずsealed interfaceとrecordでやってみた

以下、手順。

以下の2つのファイル用意してコンパイル

A.java

package example;

public sealed interface A {
  public record B(int x) implements A{}
}

F.java

package example;

public class F {
  public static int f(A a) {
    return switch(a) {
      case A.B b -> b.x();
    };
  }
}

次に A.javaF.java を以下のように改変してコンパイル。また、 Main.java 用意

 
 public sealed interface A {
   public record B(int x) implements A{}
+  public record C(int x) implements A{}
 }
   public static int f(A a) {
     return switch(a) {
       case A.B b -> b.x();
+      case A.C c -> c.x();
     };
   }

Main.java

package example;

public class Main {
  public static void main(String[] args) {
    System.out.println(F.f(new A.C(3)));
  }
}

そして

  • 古いほうの F ( = 分岐が B の1つしか書いてないもの)
  • 新しい方の AMain ( = C が増えてる。 C をメソッドに渡してる)

を組み合わせて実行する。と以下のようなエラーになる

https://github.com/xuwei-k/java-lang-MatchException-example/commit/b45756c986422d81fc00c88cd816ed8fbd3d7d75

Exception in thread "main" java.lang.MatchException
    at example.F.f(F.java:5)
    at example.Main.main(Main.java:5)

ところで、javapしてもエラーメッセージからしても、せっかく引数があるのに null しか渡してない気がするのはどうして・・・?

        44: new           #17                 // class java/lang/MatchException
        47: dup
        48: aconst_null
        49: aconst_null
        50: invokespecial #19                 // Method java/lang/MatchException."<init>":(Ljava/lang/String;Ljava/lang/Throwable;)V
        53: athrow