概要

業務コードで「状態」や「区分」を扱うとき、文字列定数や int 定数を並べて管理しているプロジェクトは少なくありません。しかし定数が増えるほどタイポや比較ミスが入り込みやすくなり、保守コストが静かに膨らんでいきます。Java の Enum はこうした問題に対する標準的な回答で、型安全な定数グループを定義し、switch 文で網羅性チェックまで得られます。この記事では、Enum の基本的な定義方法から values() による全要素取得、valueOf() による文字列変換、name() と ordinal() の違いと注意点、そして switch 文・switch 式での分岐パターンまでを扱います。Java 8 の従来型 switch と Java 14 以降の switch 式の書き分けも示しながら、現場で迷いやすいポイントを整理します。

使いどころ

注文ステータス(未処理・処理中・完了・キャンセル)を Enum で定義し、画面表示やバッチ処理の分岐に使う

曜日や休日種別を Enum にまとめ、勤怠管理システムの休日判定ロジックを型安全に記述する

CSV 取込時に文字列カラムを valueOf で Enum に変換し、不正値を早期に検出する

コード例

EnumBasicExample.java
public class EnumBasicExample {

    // 注文ステータスの Enum
    enum OrderStatus {
        PENDING,    // 未処理
        PROCESSING, // 処理中
        COMPLETED,  // 完了
        CANCELLED;  // キャンセル

        public boolean isTerminal() {
            return this == COMPLETED || this == CANCELLED;
        }
    }

    public static void main(String[] args) {
        // 基本操作: name(), ordinal(), values(), valueOf()
        var status = OrderStatus.PROCESSING;
        System.out.println("名前: " + status.name());       // PROCESSING
        System.out.println("順序: " + status.ordinal());     // 1

        // values() で全要素を取得
        for (var s : OrderStatus.values()) {
            System.out.println(s.name() + " (ordinal=" + s.ordinal() + ")");
        }

        // valueOf() で文字列から変換
        var fromStr = OrderStatus.valueOf("COMPLETED");
        System.out.println("文字列から変換: " + fromStr);

        // == で比較(equals() より安全)
        System.out.println(status == OrderStatus.PROCESSING); // true

        // switch 式で分岐(Java 14+)
        String label = switch (status) {
            case PENDING    -> "注文待ち";
            case PROCESSING -> "処理中";
            case COMPLETED  -> "完了";
            case CANCELLED  -> "キャンセル";
        };
        System.out.println("ステータス: " + label);
        System.out.println("終端状態: " + status.isTerminal()); // false
    }
}

Java 8 / 17 / 21 の完全なサンプルコードは GitHub リポジトリ で確認できます。

Version Coverage

switch 式(-> 記法)が使え、break 不要で値を返せる。var による型推論も可能。網羅性チェックがコンパイル時に効く。

Java 17
// Java 17: switch 式(-> 記法、break 不要)
var status = OrderStatus.PROCESSING;
String label = switch (status) {
    case PENDING    -> "注文待ち";
    case PROCESSING -> "処理中";
    case COMPLETED  -> "完了";
    case CANCELLED  -> "キャンセル";
};

Library Comparison

標準 Enum定数の数が固定的で、型安全な分岐や比較が目的のとき。フィールドやメソッドの追加は可能だが、実行時に要素を追加することはできない。
static final 定数ごく少数の値だけで、Enum を定義するほどでもないとき。型安全性がなく、switch の網羅性チェックも効かない。定数が増えると管理が破綻しやすい。
lombok @Getter + Enumgetter のボイラープレートを減らしたいとき。lombok 依存が入る。IDE の補完やリファクタリングとの相性に注意が必要。

注意点

valueOf() に存在しない文字列を渡すと IllegalArgumentException が発生する。外部入力を変換する場合は try-catch か事前チェックが必要

ordinal() の値は定義順に依存するため、DB 保存やシリアライズに使うと定義順の変更でデータが壊れる。永続化には name() か明示的なコード値を使うこと

switch 文で Enum を扱う場合、case ラベルに Enum 名だけを書く(Color.RED ではなく RED)。修飾名を書くとコンパイルエラーになる

Enum の == 比較は equals() と同じ結果を返し、null に対して NullPointerException を起こさない点で == のほうが安全。ただし null チェック自体は省略しないこと

FAQ

Enum の比較は == と equals() のどちらを使うべきですか。

== を使うのが一般的です。Enum はシングルトンが保証されており、== で正しく比較できます。null に対して NullPointerException を起こさない点でも == が安全です。

Enum に定義できるメソッドの数に制限はありますか。

言語仕様上の制限はありません。ただし、Enum に過度にロジックを詰め込むと責務が膨らむので、複雑な処理は別クラスに切り出す設計が実務では好まれます。

valueOf で大文字小文字を無視して変換できますか。

標準の valueOf は大文字小文字を区別します。大文字小文字を無視したい場合は、入力値を toUpperCase() してから渡すか、独自の fromName メソッドを用意してください。

関連書籍

この記事のテーマをさらに深く学びたい方へ。

※ Amazon アソシエイトリンクを含みます