工程師小劉收到一個任務,要為公司極具潛力的網站設計一個”使用者權限系統”。
因為公司的網站實在太有潛力,一上線肯定火爆全網,加上 PM 要求的也不多,就三個角色:
ADMIN、USER、GUEST,於是小劉做了一個合理的設計
class User {
String role;
}
然後登入的時候判斷權限就這樣判斷
if (!"GUEST".equals(user.getRole())) {
// 有權限囉,嗚呼
}
合理到不行,在測試階段這樣的設計一點問題都沒有。
非法值混入 + 邏輯漏洞
但是有一天,刺頭工程師”小寶寶” 在修改Bug的時候打錯字了,老哥少打了一個字母
user.setRole("GUET");
就這樣,一個不屬於預料之中的角色”GUET” 被當成有權限的單位,在網站內叱吒風雲。
語意分裂
接著,佛系工程師基佛馬上就發現小寶寶製造出來的Bug,但是他什麼都沒有說,因為問題不是他造出來的,而且他想著既然角色是 String ,那我這樣寫也是很合理的吧?
if ("ADMIN".equals(role)) { ... }
if ("SUPER_ADMIN".equals(role)) { ... }
基佛可說是比小寶寶還邪惡許多啊,因為這樣的程式碼,可說是整個權限系統失控的起點。
從此以後,整個專案充斥著各個工程師自定義、打錯的的角色名稱,只要蔓延到整個專案,就會牽一髮而動全身。
看到這裡,你大概已經發現問題了。
整個問題的根源不是出在打錯字的小寶寶,也不是出在讓錯誤更加失控的基佛
而是從一開始就沒有把規則定義明確的小劉,因為系統根本沒有定義「合法的角色值到底有哪些」
只要 role 還是 String,那麼理論上任何字串都能塞進去:
user.setRole("GUEST");
user.setRole("GUET");
user.setRole("SUPER_ADMIN");
user.setRole("I_AM_THE_CHIKENKING");
編譯器不會阻止你,IDE 不會提醒你,只有小劉會
而當整段邏輯失去一致性,這時候就輪到 Enum登場了
Enum 是什麼?它解決了什麼問題?
在 Java 裡,Enum 很適合拿來表示固定且有限的選項。 我們來看一下 java doc 是怎麼說 Enum的

enum 類型是一種特殊的資料型別,允許變數只能取一組預先定義好的常數集合中的某一個值。換句話說,該變數的值必須是事先定義好的其中之一。常見的例子包括方位(如 NORTH、SOUTH、EAST、WEST)以及一週的日期。
像上述的角色權限系統,其實就很符合 Enum 的使用情境,如果把角色改成 Enum,程式會變成這樣:
enum Role {
ADMIN,
USER,
GUEST
}
class User {
private Role role;
public User(Role role) {
this.role = role;
}
public Role getRole() {
return role;
}
public void setRole(Role role) {
this.role = role;
}
}
這樣一來,基佛想要做這種操作就會直接失效了:
user.setRole(Role.GUEST); // 合法
user.setRole(Role.ADMIN); // 合法
user.setRole("I_AM_THE_CHIKENKING"); // 編譯錯誤
看起來只是把 String 換成 Role,但背後其實是把「角色只能有哪些值」這件事,從人腦記憶變成了型別系統的一部分。
這也是 Enum 最核心的價值:
把合法值限制在一個明確範圍內
使用 Enum 後,程式碼會有什麼改變?
最直接的改變就是判斷邏輯會變得更明確。
相比這個
if ("ADMIN".equals(role)) { ... }
if ("SUPER_ADMIN".equals(role)) { ... }
改成 Enum 之後,可以寫成:
if (user.getRole() != Role.GUEST) {
// 亂寫的話,你連編譯都過不了哦><
}
這種寫法有幾個好處。
第一,可讀性更高。
你看到 Role.GUEST,會很清楚這是一個受控的角色值,不是一段看工程師心情決定的值。
第二,重構更安全。
如果未來角色名稱要調整,更快速清楚,尤其現在各式各樣的IDE都很強,一鍵重構,不用再搜尋整個專案找哪裡受到波及。
第三,維護成本更低。
團隊成員不需要再記住「到底有哪些角色」,因為程式碼本身就已經把答案寫出來了。
兩個和 Enum 很搭的實用工具
EnumSet:專門用來存放 Enum 的集合
如果你想表示「哪些角色有權限」,直覺上可能會先想到 Set<Role>:
Set<Role>allowedRoles=newHashSet<>();
allowedRoles.add(Role.ADMIN);
allowedRoles.add(Role.USER);
這樣寫當然可以,但如果集合裡放的就是 Enum,那其實可以使用 EnumSet 。
importjava.util.EnumSet;
importjava.util.Set;
Set<Role>allowedRoles=EnumSet.of(Role.ADMIN,Role.USER);
if (allowedRoles.contains(user.getRole())) {
System.out.println("允許存取");
}
EnumSet 是一個專門為 Enum 設計的集合,它的好處是:
- 語意更明確,一看就知道這是一組列舉值
- 寫起來簡潔
- 效能通常也比一般的
HashSet更適合這類場景
當角色越來越多時,EnumSet 會更好維護。
EnumMap:當 key 是 Enum 時,比 HashMap 更適合
另一個很常見的需求是:
不同角色,對應不同的資料。
例如:
- 不同角色對應不同的顯示名稱
- 不同狀態對應不同的提示訊息
- 不同權限對應不同的說明文字
這時候,如果 Map 的 key 是 Enum,可以使用 EnumMap。
importjava.util.EnumMap;
importjava.util.Map;
Map<Role,String> roleDescriptions = new EnumMap<>(Role.class);
roleDescriptions.put(Role.ADMIN,"系統管理員");
roleDescriptions.put(Role.USER,"一般使用者");
roleDescriptions.put(Role.GUEST,"訪客");
System.out.println(roleDescriptions.get(Role.ADMIN));// 系統管理員
EnumMap 和 EnumSet 一樣,都是專門為 Enum 設計的工具。
當 key 本來就是固定且有限的 enum 值時,EnumMap 會比 HashMap 更貼近需求。
小結
Enum 幫我們限制合法值,
而 EnumSet 和 EnumMap 則是讓這些合法值在集合與對應關係中,能被更自然地使用。

發佈留言