Java 列挙型(enum)クラス使い方、構造について

2025 年 2 月 25 日 by marukor

はじめに

なんとなくで使用していたenumクラス。その実態から使い方までしっかり理解して使っていきたいと思ったので調査してみました。この記事を読めばあなたもenumクラスが使いたくなることでしょう。

バージョン

java version “1.8.0_191”

os: windows11

列挙型(enum)クラスとは

enum(列挙型)は、特定の値の集合を定義するためのクラスです。
例えば、曜日、色、状態など、限られた値のセットを持つ場合に適しています。

使い方

それでは使い方を見ていきます。

基本的な enum の使い方
曜日を判定するプログラム

public enum Day {
    SUNDAY, 
    MONDAY, 
    TUESDAY, 
    WEDNESDAY, 
    THURSDAY, 
    FRIDAY, 
    SATURDAY;
}

public class Main {
    public static void main(String[] args) {
        Day today = Day.WEDNESDAY;
        System.out.println("Today is " + today);

        if (today == Day.SATURDAY || today == Day.SUNDAY) {
            System.out.println("It's a weekend!");
        } else {
            System.out.println("It's a weekday.");
        }
    }
}

フィールドを直接呼出しただけの場合 実行結果

Today is WEDNESDAY
It’s a weekday.

enum にフィールドやメソッドを持たせる
注文ステータスを管理する プログラム

public enum OrderStatus {
    PENDING(1, "処理中"),
    SHIPPED(2, "発送済み"),
    DELIVERED(3, "配達完了"),
    CANCELED(4, "キャンセル");

    private final int code;
    private final String description;

    OrderStatus(int code, String description) {
        this.code = code;
        this.description = description;
    }

    public int getCode() {
        return code;
    }

    public String getDescription() {
        return description;
    }

    public static OrderStatus fromCode(int code) {
        for (OrderStatus status : OrderStatus.values()) {
            if (status.getCode() == code) {
                return status;
            }
        }
        throw new IllegalArgumentException("Invalid code: " + code);
    }
}

public class Main {
    public static void main(String[] args) {
        OrderStatus status = OrderStatus.SHIPPED;
        System.out.println("Order Status: " + status.getDescription());

        // 数値から `enum` を取得
        OrderStatus statusFromCode = OrderStatus.fromCode(3);
        System.out.println("Status from code: " + statusFromCode);
    }
}

フィールドの値を取得し、そのメソッドから値を取得した実行結果

Order Status: 発送済み
Status from code: DELIVERED

switch 文で enum を使う

public class Main {
    public static void main(String[] args) {
        OrderStatus status = OrderStatus.DELIVERED;

        switch (status) {
            case PENDING:
                System.out.println("注文はまだ処理中です。");
                break;
            case SHIPPED:
                System.out.println("注文は発送済みです。");
                break;
            case DELIVERED:
                System.out.println("注文が配達されました。");
                break;
            case CANCELED:
                System.out.println("注文がキャンセルされました。");
                break;
        }
    }
}

これは現場でよく見るパターンです。

switch文で分岐させた場合 実行結果

注文が配達されました。

enumクラスの使用例はおおよそこのような形です。

ここからはenumクラスの内部について見ていきます。

構造

以下のようなコードがあったとします。

OrderStatus status = OrderStatus.SHIPPED; 

ここでの status は、OrderStatus 型のオブジェクトです。 OrderStatus のインスタンス(列挙定数)になります。 OrderStatus.SHIPPED は OrderStatus の定数であり、実際には OrderStatus クラスのオブジェクト(インスタンス) です。

ではOrderStatus.SHIPPED のような enum の実体がどのように生成・管理されているのかを詳しく説明します。

enum の内部構造

例えば、次の enum を考えます。

public enum OrderStatus {
    PENDING(1, "処理中"),
    SHIPPED(2, "発送済み"),
    DELIVERED(3, "配達完了"),
    CANCELED(4, "キャンセル");

    private final int code;
    private final String description;

    OrderStatus(int code, String description) {
        this.code = code;
        this.description = description;
    }

    public int getCode() {
        return code;
    }

    public String getDescription() {
        return description;
    }
}

OrderStatus のコンパイル後のイメージ

Javaの enum は、内部的には class のように扱われ、各定数 (PENDING, SHIPPED, DELIVERED, CANCELED) は静的なインスタンスとして管理されます。

Java コンパイラが変換するイメージ

public final class OrderStatus extends Enum<OrderStatus> {
    public static final OrderStatus PENDING = new OrderStatus("PENDING", 0, 1, "処理中");
    public static final OrderStatus SHIPPED = new OrderStatus("SHIPPED", 1, 2, "発送済み");
    public static final OrderStatus DELIVERED = new OrderStatus("DELIVERED", 2, 3, "配達完了");
    public static final OrderStatus CANCELED = new OrderStatus("CANCELED", 3, 4, "キャンセル");

    private final int code;
    private final String description;

    private OrderStatus(String name, int ordinal, int code, String description) {
        super(name, ordinal); // Enum クラスのコンストラクタを呼ぶ
        this.code = code;
        this.description = description;
    }

    public int getCode() { return code; }
    public String getDescription() { return description; }

    public static OrderStatus[] values() {
        return new OrderStatus[]{ PENDING, SHIPPED, DELIVERED, CANCELED };
    }

    public static OrderStatus valueOf(String name) {
        for (OrderStatus status : values()) {
            if (status.name().equals(name)) {
                return status;
            }
        }
        throw new IllegalArgumentException("No enum constant " + name);
    }
}

status の実体

OrderStatus status = OrderStatus.SHIPPED;

この時、status の実態は OrderStatus.SHIPPED という静的インスタンスになります。

実際のインスタンスの中身

OrderStatus.SHIPPED = new OrderStatus("SHIPPED", 1, 2, "発送済み");

実際はこのような流れになっていたわけです。
クラスロード時に SHIPPED などの静的インスタンスが一度だけ生成され、以降はその static final フィールドを参照しているので、 OrderStatus status = OrderStatus.SHIPPED; のような呼び出し方になっているということでした。
これでより理解してenumクラスを使うことができるようになりました。

タグ:

TrackBack