ソフトウェアエンジニアの日常の雑記

日々思ったことをまとめます

JavaのEnumとSwitch式の良い関係

はじめに

今回は、私が好きで多用しているEnumとSwitch式の良い関係を書きます。

enum

Enumは、Java1.5(Tiger)のときに入った機能で、EffectiveJava(第3版)でも、第6章 enumとアノテーションアノテーションとセットですが、1章を割かれるくらいの機能になります。他の言語だとenumにはあまりスポットがあたっているのを見かけませんが、最近だとFlutter3にJavaと同じようなenumの機能が入っていました。

enumのよく使うケース

システム上で固定で判定できるものは、ほとんどenumで対応したりしています。下記はシステム上で使用するステータスを管理するenumになります。

public enum StatusType {
    TODO("作業前")
    , DOING("作業中")
    , CHECK("確認中")
    , DONE("完了")
    , OTHER("その他");

    private static final Map<String, StatusType> STATUS_TYPE_MAP = Arrays.stream(StatusType.values()).collect(Collectors.toMap(e -> e.name(), e -> e));  // 初期化時にMapに入れて取り出しやすくします
    
    private final String displayName;

    StatusType(String displayName) {
        this.displayName = displayName;
    }

    // データベース等に入っている文字列をこのメソッドを通すことで、enum型に変換します
    public StatusType getStatusType(String name) {
        return STATUS_TYPE_MAP.getOrDefault(name.toUpperCase(), StatusType.OTHER);
    }
}

このような感じのenumをシステムの挙動にあわせて、結構作成します。アプリケーション側ではenumの値によって処理を変えるということをやるときには下記のようになります。

class Sample {

    List<String> index(StatusType statusType){
        return switch (statusType) {
            case TODO -> todo();
            case DOING -> doing();
            case CHECK -> check();
            case DONE -> done();
            case OTHER -> other();
        }
    }
    
    // 各メソッドのみ記述(処理は省きます)
    private List<String> todo(){ return List.of();}
    private List<String> doing(){ return List.of();}
    private List<String> check(){ return List.of();}
    private List<String> done(){ return List.of();}
    private List<String> other(){ return List.of();}
}

enumのステータスが追加されることになったとき

上記のようなenumのステータスごとに分岐する処理があちらこちらに書いてあるとします。そのときに、enumの値が追加されると漏れなく全てを書き直さないといけません。enumとswitch式を使用している、enumに定義している定数を追加した際に、switch式の定義が足りないとコンパイルエラーが発生します。

public enum StatusType {
    TODO("作業前")
    , DOING("作業中")
    , CHECK("確認中")
    , CHECKED("確認済")    // <- ステータスの追加を行う
    , DONE("完了")
    , OTHER("その他");

(中略)

}

上記のように、ステータスの追加を行ってコンパイルをしてみましょう。

エラー: switch式がすべての可能な入力値をカバーしていません
        return switch (statusType) {
               ^

switch式がenumの全て要素の処理が書かれていないので、コンパイルエラーになります。これで、ステータスが追加されても処理の抜け漏れがなくて安全にステータスの追加や削除が行なえます。安心安全の型システムの恩恵を享受できています。

まとめ

enumは列挙型であるとともに、switch式と組み合わせると網羅性も担保されており、ドメイン知識が多いシステムのときにぬけもれがなく安心して開発ができるとても手軽で便利な機能だと思います。最近では、recordsealedを組み合わせて、enumでなくても網羅性のあるコードが書けるようになってきています。頑強なシステムを開発するのに重要な機能だと思いますので使いこなしていきたいです。

※この網羅性の機能はswitch式のみで使用できる機能であり、switch文では使用できません。

さいごに

久しぶりに個人ブログを書きました。しばらくは、所属会社の方のテックブログを頑張っていたのですが、これを機に個人ブログも再開しようかと思います。来年も良いJava開発ライフであると良いなと思います。

良いお年を!