0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Effective Java 6.34:int定数の代わりにenumを使う

Posted at

6.34:int定数の代わりにenumを使う

要点

int 定数(public static final int ...)を列挙的に表すなら enum を使う。enum のほうが型安全・可読性・拡張性・シリアライズの安全性で優れる。

なぜ enum が良いのか

  • 型安全:
    int はどんな値でも代入できるが、enum は定義した列挙子しか取り得ないので、誤った値を渡すミスをコンパイル時に防げる。
  • 名前空間の管理:
    列挙子はその enum 型のスコープに属するため名前衝突やグローバルな定数の氾濫を防げる。
  • 振る舞いを持てる:
    enum はフィールド・メソッドを持てる。値ごとの挙動(抽象メソッドをオーバーライド)も可能。
  • デバッグしやすい・可読性が高い:
    toString() や名前がそのまま出るのでログやデバッグ時に直感的。
  • シリアライズが安全:
    enum は名前でシリアライズされるため、通常の Serializable よりも安全で、複製されることはない(シングルトン性が守られる)。
  • コレクションとの親和性:
    EnumSet / EnumMap など専用高速コレクションがある(ビット演算より安全で効率的)。
  • switch と相性が良い:
    switch(enum) は可読で扱いやすい(Java ではサポートあり)。

悪い例

古い int 定数パターン

public class Planet {
    public static final int MERCURY = 0;
    public static final int VENUS   = 1;
    public static final int EARTH   = 2;
    // ... 多数の定数
}

// 呼び出し側
void travel(int planet) { ... }
travel(2); // 何の 2 か分かりにくい、誤値を渡せてしまう

問題点:
travel(999) や travel(-1) をコンパイルは通してしまう。ログに出るのは数値のみで意味が分かりにくい。

良い例

enum を使う

public enum Planet {
    MERCURY, VENUS, EARTH, MARS, JUPITER, SATURN, URANUS, NEPTUNE;
}

// 呼び出し側
void travel(Planet planet) { ... }
travel(Planet.EARTH); // 型安全で可読性良し

さらにフィールドやメソッドを追加できる:

public enum Planet {
    MERCURY(3.30e+23, 2.4397e6),
    VENUS  (4.87e+24, 6.0518e6),
    EARTH  (5.97e+24, 6.37814e6);
    // ... 

    private final double mass;   // kg
    private final double radius; // meters

    Planet(double mass, double radius) {
        this.mass = mass;
        this.radius = radius;
    }
    public double surfaceGravity() { /* 計算 */ }
    @Override public String toString() { return name().toLowerCase(); }
}

→ データと振る舞いをひとまとめにできる。

EnumSet / EnumMap を使おう(ビットマスクの代替)

  • よくあるパターン:
    複数選択のフラグを int のビットマスクでやるケース。enum + EnumSet の方が読みやすく安全。
public enum Permission { READ, WRITE, EXECUTE }

EnumSet<Permission> perms = EnumSet.of(Permission.READ, Permission.WRITE);
if (perms.contains(Permission.EXECUTE)) { ... }

メリット: ビット演算より安全で、可読性も高く、EnumSet は内部的にビット集合に最適化されていて高速。

注意点・ベストプラクティス

  • ordinal() を永続化に使うな:
    enum の ordinal()(定義順の整数値)は実装に依存するし、後で順序を変えたら壊れる。永続化や外部 API とやり取りする時は明示的なコード(フィールド)を持たせ、fromCode(int) のような変換メソッドを提供する。
public enum Status {
    NEW(0), IN_PROGRESS(1), DONE(2);
    private final int code;
    Status(int code) { this.code = code; }
    public int code() { return code; }
    public static Status fromCode(int code) { /* map lookup */ }
}
  • 外部との互換性(プロトコル・DB):
    既に int を使って外部に流している場合は、enum に変換しても内部で int code を保持し、fromCode を用意するとスムーズ。
  • シリアライズ:
    enum は Java のシリアライズで名前ベースに扱われるため比較的安全。ただし名前を変更すると互換性が壊れるので注意(DB 等に文字列を保存する場合も同様)。
  • パフォーマンスは心配不要:
    enum は軽量で、通常パフォーマンス問題にはならない。
  • switch 文で扱うとき:
    switch(enum) は推奨される使い方。枚挙される全ケースをコンパイル時に把握できる。

まとめ

  • 列挙的な選択肢を表すなら enum を第一選択 に。
  • int 定数は可読性・安全性の面で劣る。
  • 複数選択フラグなら EnumSet を使う。
  • 永続化・外部 API とのやり取りがあるなら ordinal() ではなく明示的なコードフィールドを使って変換する。
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?