Javaで代数的データ型は使えるようになるのか
はじめに
みなさんは代数的データ型(Algebraic Data Type; ADT)をご存知でしょうか?[1]私はADTを使ってからもうADTがない言語でコーディングしたくなくなってしまい大変困っています.
Javaは業界で広く使われているようなので,JavaにADTがあったらなあと思うわけですが,ちらほらJava界隈でパターンマッチという用語を耳にするようになりました.
そこでもしかしてJavaでADTが使えるようになるんじゃないか?と思い,調べて見ました.
結論
書けるっちゃ書ける..のか?
JavaにおけるADTっぽい機能
Sealed Classes/Interfaces
Sealed Classes/Interfacesは継承,実装するクラスを制限できるクラス・インターフェイスです.Java17で正式な機能となりました.継承,実装するクラスを制限することでパターンマッチのときに網羅性をチェックすることができるようになります.
Pattern Matching for switch
Javaのパターンマッチ(=switch文/式)は数字,String,enumしか取れず,caseラベルは定数しか許されていませんでしたが,Java 17でpreview featureとして任意の型を取れるようになり,caseラベルはパターンを取れるようになりました.
Resultは書けるか?
これらの機能を使ってRustのResult相当のものを書いてみました.
コードはIntelliJ IDEA 2022.1で実行,sdkはopenjdk-18 version18.0.1,Language Levelは18(Preview) - Pattern matching for switch (second preview)です.
sealed interface Result<T, E> {
}
record Ok<T, E>(T t) implements Result<T, E> { }
record Err<T, E>(E e) implements Result<T, E> { }
public class Main {
public static void main(String[] args) {
var result = div(6.0, 3.0);
var answer = switch (result) {
//冗長な記述
case Ok<Double, String> ok -> ok.t();
case Err<Double, String> err -> throw new RuntimeException(err.e());
};
System.out.println(answer);
}
//型エイリアスがない
static Result<Double, String> div(double a, double b) {
if (Double.compare(b, 0.0) == 0) {
return new Err<>("Division by zero");
}
return new Ok<>(a / b);
}
}
2.0
書けているっちゃ書けている気もしますが,型エイリアスが無いのが辛いのと,caseにパラメータを書かなければいけない気がするのも冗長で辛いです..
終わりに
この記事ではJavaで代数的データ型が使えるのか調べて見ました.まあ使えるっちゃ使えるのかな?という感じでした.Sealed ClassesとPattern Matching for switchはなかなか良さそうな機能ですね.
JEP427:Pattern Matching for switch (Third Preview)のFuture workセクションでは以下のような脱構築パターンも示されていて,より使い勝手が増しそうです.
int eval(Expr n) {
return switch (n) {
case IntExpr(int i) -> i;
case NegExpr(Expr n) -> eval(n);
case AddExpr(Expr left, Expr right) -> eval(left) + eval(right);
case MulExpr(Expr left, Expr right) -> eval(left) * eval(right);
default -> throw new IllegalStateException();
};
}
参考文献
-
「代数的データ型」でググるといろいろな記事が出てくるのでぜひ調べてみてください. ↩︎
Discussion