整理Effective Java書中Item 35: Use instance fields instead of ordinals心得筆記

主旨

雖然 enum.ordinal() 這個方法看起來很方便,可以讓你直接取得列舉常數的順序編號,但實際上使用它來表示邏輯意義是非常危險的作法。這一則要點告訴我們:如果你需要用 enum 來代表某種數值或邏輯資訊,應該用實例欄位來明確定義,而不要依賴 ordinal() 的數字順序。

點出問題:ordinal() 表面簡單,其實是地雷

我們先來看一個錯誤使用 ordinal() 的例子:

public enum Level {
    LOW, MEDIUM, HIGH
}

// 根據 ordinal() 來決定危險等級數字
int danger = Level.HIGH.ordinal();  // 這會是 2

乍看之下沒什麼問題,但如果未來有一天你需要調整 enum 的順序,例如把 HIGH 放到最前面,結果 danger 的值也會變成 0,導致整個邏輯大錯特錯,而且這種 bug 不容易察覺。

這種「依賴順序」的設計,未來只要有人「整理一下 enum 順序」都可能造成災難。

範例:用實例欄位更穩定

正確做法是這樣,透過建構子和欄位明確定義每個常數對應的值:

public enum Level {
    LOW(1), MEDIUM(5), HIGH(10);

    private final int dangerValue;

    Level(int dangerValue) {
        this.dangerValue = dangerValue;
    }

    public int getDangerValue() {
        return dangerValue;
    }
}

這樣一來就算你把 HIGH 搬到最前面,也不會影響邏輯,因為 dangerValue 是明確設定的:

int danger = Level.HIGH.getDangerValue();  // 永遠是 10,不會因為順序變動

小結

使用 ordinal() 雖然方便,但它不是為了邏輯用途而設計的,而是為了內部 JVM 使用或做排序時使用的。如果你用它來判斷值或做資料儲存,一旦 enum 的順序改變,你的程式就會壞掉。

✅ 該怎麼做:

  • 如果 enum 常數需要有數值意義(例如危險程度、顯示順序、代碼),請用實例欄位定義。
  • 請避免使用 ordinal() 做為資料儲存或邏輯條件依據。
  • 對外暴露資料時,也應該使用自定義欄位,而非 ordinal()