Java 5 引入 enum 關鍵字,提供類型安全的列舉,徹底取代 int 常數模式。本文系統講解列舉核心語法、豐富行為、專用集合與設計最佳實踐。
public enum Size { SMALL, MEDIUM, LARGE }
// 使用
Size current = Size.MEDIUM;
System.out.println(current); // MEDIUM
// 遍歷所有值 (宣告順序)
for (Size s : Size.values()) {
System.out.print(s + " "); // SMALL MEDIUM LARGE
}
// switch 完美支援 (無需 break 防貫穿)
switch (current) {
case SMALL: System.out.println("S"); break;
case MEDIUM: System.out.println("M"); break;
case LARGE: System.out.println("L"); break;
}
| 特性 | int 常數 | enum |
|---|---|---|
| 類型安全 | 無 (可傳入任意 int) | 編譯期保證 |
| 命名空間 | 無 (常數污染類別) | 獨立 (Size.SMALL) |
| 可列印性 | 輸出數字 | 輸出名稱 |
| 可擴充性 | 脆弱 (新增值破壞二進制相容) | 安全 (不影響現有代碼) |
| 行為豐富度 | 無 | 方法、欄位、介面實作 |
public enum Size {
SMALL(0.8), MEDIUM(1.0), LARGE(1.2), XL(1.2);
private final double pricingFactor;
Size(double factor) { this.pricingFactor = factor; }
public double getPricingFactor() { return pricingFactor; }
// 靜態工廠方法
public static Size fromFactor(double factor) {
for (Size s : values())
if (s.pricingFactor == factor) return s;
throw new IllegalArgumentException("Unknown factor: " + factor);
}
}
// 使用
double factor = Size.LARGE.getPricingFactor(); // 1.2
Size s = Size.fromFactor(1.0); // MEDIUM
public enum Operation {
PLUS { double apply(double x, double y) { return x + y; } },
MINUS { double apply(double x, double y) { return x - y; } },
TIMES { double apply(double x, double y) { return x * y; } },
DIVIDE { double apply(double x, double y) { return x / y; } };
abstract double apply(double x, double y);
}
// 使用
double result = Operation.PLUS.apply(2, 3); // 5.0
每個常數隱式為 Operation 的匿名子類,**多型行為零樣板代碼**。
所有列舉隱式繼承 java.lang.Enum<E extends Enum<E>>,獲得方法:
| 方法 | 說明 |
|---|---|
name() | 常數名稱 (如 "SMALL"),不可覆寫 |
ordinal() | 宣告順序 (0-based),不可覆寫 |
toString() | 預設回傳 name(),可覆寫 |
compareTo(E o) | 依 ordinal 比較 |
equals(Object) | 身分比較 (==) |
getDeclaringClass() | 列舉類別的 Class 物件 |
valueOf(Class, String) | 靜態方法,由名稱取得常數 |
enum Size { SMALL, MEDIUM, LARGE }
Size s = Size.SMALL;
System.out.println(s.getClass()); // class Size
System.out.println(s.getDeclaringClass()); // class Size
// 若有常數特定類別主體:
enum Op {
ADD { public String toString() { return "+"; } },
SUB { public String toString() { return "-"; } }
}
Op op = Op.ADD;
System.out.println(op.getClass()); // class Op$1 (匿名子類)
System.out.println(op.getDeclaringClass()); // class Op (宣告類別)
// 內部以陣列實現,索引 = ordinal(),無雜湊開銷
Map<Size, String> label = new EnumMap<>(Size.class);
label.put(Size.SMALL, "S");
label.put(Size.LARGE, "L");
// 遍歷保證按宣告順序
for (Map.Entry<Size, String> e : label.entrySet()) {
System.out.println(e.getKey() + " -> " + e.getValue());
}
// 工廠方法豐富
EnumSet all = EnumSet.allOf(Size.class); // 全部
EnumSet sm = EnumSet.range(Size.SMALL, Size.MEDIUM); // 範圍
EnumSet xl = EnumSet.complementOf(sm); // 補集
EnumSet custom = EnumSet.of(Size.SMALL, Size.XL); // 指定元素
// 標準 Set 操作
custom.add(Size.LARGE);
custom.remove(Size.XL);
if (custom.contains(Size.LARGE)) { /* ... */ }
EnumMap vs HashMap:讀寫快 **5-10 倍**,記憶體少 **50%+**EnumSet vs HashSet:操作快 **10 倍以上**,記憶體極小 (單 long 位元向量,最多 64 個常數)enum State { IDLE, RUNNING, PAUSED, STOPPED } + 狀態轉移方法enum Strategy { A, B, C } 每個常數實作 execute()enum Singleton { INSTANCE; ... } (Josh Bloch 推薦,防序列化破壞、線程安全)enum Command { OPEN, SAVE, EXIT } + execute()name() 或自訂代碼欄位valueOf() 還原,**免受 ordinal 變更影響**VARCHAR 存名稱 (可讀、穩定) 或 SMALLINT 存自訂代碼 (效能、空間)Java 列舉不只是「類型安全的常數」,更是功能豐富的類別,支援建構子、方法、介面實作、多型行為。配合 EnumMap/EnumSet 極致效能,是建模領域概念、實現狀態機、策略、命令等模式的首選工具。**善用列舉,寫出更安全、更具表達力、更易維護的代碼。**