為什麼選擇 Enum?
- 類型安全:編譯期限制合法值,避免字串/整數常量的任意傳入。
- 單例保證:每個枚舉常量在 JVM 中唯一,
== 比較安全高效。
- 豐富行為:可攜帶欄位、方法、實作介面,將業務邏輯封裝在枚舉內部。
- IDE 友好:自動完成、重構支援、switch 完整性檢查。
基礎定義與擴展
public enum OrderStatus {
PENDING("待付款", 1),
PAID("已付款", 2),
SHIPPED("已發貨", 3),
COMPLETED("已完成", 4),
CANCELLED("已取消", 5);
private final String displayName;
private final int code;
OrderStatus(String displayName, int code) {
this.displayName = displayName;
this.code = code;
}
public String getDisplayName() { return displayName; }
public int getCode() { return code; }
// 靜態查找輔助
private static final Map CODE_MAP = Arrays.stream(values())
.collect(Collectors.toMap(OrderStatus::getCode, s -> s, (a, b) -> a));
public static OrderStatus fromCode(int code) {
return CODE_MAP.getOrDefault(code, PENDING);
}
}
字串/外部資料轉枚舉:核心技巧
1. 標準 valueOf (區分大小寫、必須精確匹配)
OrderStatus s = OrderStatus.valueOf("PAID"); // OK
OrderStatus s = OrderStatus.valueOf("paid"); // IllegalArgumentException
2. 忽略大小寫轉換
public static OrderStatus fromStringIgnoreCase(String name) {
if (name == null) return null;
for (OrderStatus s : values()) {
if (s.name().equalsIgnoreCase(name)) return s;
}
return null; // 或拋出異常
}
3. 資料庫/前端代碼映射 (code/字串雙向)
// 已在枚舉內建立 CODE_MAP,支援 fromCode(int)
int dbCode = 3;
OrderStatus status = OrderStatus.fromCode(dbCode); // SHIPPED
4. 駝峰/下劃線命名風格自動轉換
public enum UserAction {
SAVE_USER, GET_USER_BY_ID, DELETE_USER_BY_ID;
public String toCamelCase() {
return Arrays.stream(name().split("_"))
.map(s -> s.substring(0,1).toUpperCase() + s.substring(1).toLowerCase())
.collect(Collectors.joining());
}
// SAVE_USER -> SaveUser
}
實作介面:行為封裝
public interface DiscountStrategy {
BigDecimal calculate(BigDecimal amount);
}
public enum MemberLevel implements DiscountStrategy {
NORMAL("普通會員") { public BigDecimal calculate(BigDecimal a) { return a; } },
SILVER("銀卡") { public BigDecimal calculate(BigDecimal a) { return a.multiply(new BigDecimal("0.95")); } },
GOLD("金卡") { public BigDecimal calculate(BigDecimal a) { return a.multiply(new BigDecimal("0.90")); } },
DIAMOND("鑽石") { public BigDecimal calculate(BigDecimal a) { return a.multiply(new BigDecimal("0.85")); } };
private final String desc;
MemberLevel(String desc) { this.desc = desc; }
public String getDesc() { return desc; }
}
// 使用
BigDecimal payable = MemberLevel.GOLD.calculate(new BigDecimal("1000")); // 900
進階:EnumMap / EnumSet 高效集合
// EnumMap: key 為枚舉,底層陣列,極快
EnumMap countMap = new EnumMap<>(OrderStatus.class);
countMap.put(OrderStatus.PAID, 100);
// EnumSet: 位向量實作,集合運作極快
EnumSet activeStatuses = EnumSet.of(OrderStatus.PAID, OrderStatus.SHIPPED);
activeStatuses.contains(OrderStatus.PAID); // true
序列化與反射注意事項
- 枚舉序列化僅儲存名稱,反序列化自動調用
valueOf,天然防偽造、單例不破壞。
Enum.valueOf(Class<T>, String) 為反射入口,性能優於一般反射。
最佳實踐清單
- 枚舉常量名全大寫下劃線 (
PENDING_PAYMENT)。
- 欄位宣告
private final,提供 getter,避免 setter** (破壞單例線程安全)。
- 複雜查找邏輯建立
static final Map 快取,靜態區塊初始化。
- switch 盡量無 default,新增枚舉時編譯器提醒漏處理 case。
- 大量枚舉常量時考慮拆分為多個枚舉類別,避免類膨脹。