java-recipes

ホーム Enum(列挙型) › En-01

En-01: Enum の基本(定数セット)

Java の enum は、 関連する定数をひとつの型としてまとめる仕組みです。int 定数やString 定数と比べて 型安全で、IDE の補完やリファクタリングもしやすくなります。

いつ使うか

  • 注文ステータス・支払方法・曜日など、取りうる値が決まっている定数セットを定義するとき
  • switch 文や switch 式でケースを網羅させたいとき
  • 定数に独自のメソッドやフィールドを持たせたいとき(En-02 で詳しく解説します)

int 定数 / String 定数 / Enum の比較

特性int 定数String 定数Enum
型安全性なし(任意の int を渡せる)なし(任意の文字列を渡せる)あり(定義した値のみ)
switch 全網羅チェックなしなしあり(switch 式)
IDE 補完
メソッド追加不可不可可能

サンプルコード

EnumBasicSample.java
public class EnumBasicSample {

    // 基本的な Enum の定義
    enum Color {
        RED, GREEN, BLUE
    }

    // 曜日の Enum
    enum DayOfWeekJp {
        MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY;

        // Enum にメソッドを追加できる
        public boolean isWeekend() {
            return this == SATURDAY || this == SUNDAY;
        }
    }

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

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

    public static void main(String[] args) {
        // 基本的な使い方
        Color color = Color.RED;
        System.out.println("色: " + color);                  // RED
        System.out.println("名前: " + color.name());         // RED
        System.out.println("順序: " + color.ordinal());      // 0

        System.out.println("\n--- values() で全要素取得 ---");
        for (Color c : Color.values()) {
            System.out.println(c.name() + " (ordinal=" + c.ordinal() + ")");
        }

        System.out.println("\n--- valueOf() で文字列から変換 ---");
        Color fromStr = Color.valueOf("GREEN");
        System.out.println("GREEN から変換: " + fromStr);

        // Enum は == で比較できる(equals() 不要)
        System.out.println("\n--- == による比較 ---");
        System.out.println(color == Color.RED);    // true
        System.out.println(color == Color.BLUE);   // false

        System.out.println("\n--- 曜日: isWeekend() ---");
        for (DayOfWeekJp day : DayOfWeekJp.values()) {
            System.out.println(day + ": " + (day.isWeekend() ? "休日" : "平日"));
        }

        System.out.println("\n--- switch 文での使用 ---");
        OrderStatus status = OrderStatus.PROCESSING;
        switch (status) {
            case PENDING:
                System.out.println("注文待ち");
                break;
            case PROCESSING:
                System.out.println("処理中");
                break;
            case COMPLETED:
                System.out.println("完了");
                break;
            case CANCELLED:
                System.out.println("キャンセル");
                break;
        }
        System.out.println("終端状態: " + status.isTerminal()); // false
    }
}

Java 8 から Enum に独自メソッドを追加できます。switch 文で Enum を使うと、コンパイラがすべてのケースを網羅しているか確認できます(警告が出ます)。

よくあるミス・注意点

Enum は == で比較する

Enum は同一インスタンスが保証されているため、== で比較できます。equals() でも動作しますが、 Enum の場合は == が慣例です。null との比較を== で行う場合は NullPointerException が発生しないので安全です。

ordinal() を DB に保存するのは危険

ordinal() は定義順の整数(0 始まり)を返しますが、 後から定数の順序を変更したり途中に挿入したりすると値がずれて既存データが壊れます。 DB に保存するときは name()(文字列)か、 En-02 で紹介する専用コードフィールドを使いましょう。

valueOf() に存在しない文字列を渡すと例外が発生する

Color.valueOf("UNKNOWN") のように 定義されていない文字列を渡すとIllegalArgumentException がスローされます。 外部入力(API レスポンス・ファイル)から変換する場合は try-catch するか、 事前に Arrays.stream(values()).anyMatch(...) で検証しましょう。

switch 式(Java 14+)では Enum の全ケースを必ず書く

switch 式で Enum を使う場合、全ケースを列挙すれば default は不要です。 ケースが不足していると「not exhaustive(網羅的でない)」というコンパイルエラーになります。 これにより、新しい Enum 定数を追加したときにコンパイルエラーで気付けるという安全性が得られます。

テストする観点

  • values() が全定数を定義順どおりに返すこと
  • ordinal() が 0 始まりの定義順整数を返すこと(境界値: 先頭・末尾)
  • valueOf() で正しい定数名を渡したとき、対応する Enum が返ること
  • valueOf() で存在しない文字列を渡したとき、IllegalArgumentException がスローされること
  • isWeekend() が SATURDAY・SUNDAY で true、それ以外で false を返すこと
  • isTerminal() が COMPLETED・CANCELLED で true、PENDING・PROCESSING で false を返すこと
  • Enum の == 比較が同じ定数では true、異なる定数では false を返すこと

GitHub でソースコードを見る →