1. 導言
在程式設計中,我們經常需要處理一組事先已知且有限的選項。例子包括:一週的天數、紅綠燈顏色、遊戲難度等級、訂單狀態。這些選項通常以固定值表示。這正是我們今天的主題——列舉(enum)。
列舉 是一種特殊的資料型別,用於宣告一組具名常數。
enum 就像一個把標籤分格整理好的盒子,每個標籤都是獨一無二的名稱,並且對應到一個數字。
enum 的主要優點:
- 你的程式碼更可讀、更自我說明;
- 不再有讓人摸不著頭緒的「魔術數字」如 0、1、2,而是具表意性的名稱:DayOfWeek.MONDAY 或 TrafficLight.RED;
- 當你嘗試指定不正確的值時,編譯器會報錯;
- 每個元素背後其實就是一般的整數!(稍後再談。)
enum 宣告語法
// 列舉宣告的一般範本
public enum Imya
{
KONSTANTA1 ,
KONSTANTA2 ,
KONSTANTA3
}
在 Java 宣告列舉非常簡單。以下是基本範例:
// 檔案 DayOfWeek.java
public enum DayOfWeek {
MONDAY, // 0
TUESDAY, // 1
WEDNESDAY, // 2
THURSDAY, // 3
FRIDAY, // 4
SATURDAY, // 5
SUNDAY // 6
}
說明:
- 關鍵字 enum 告訴編譯器:「這是一個列舉!」
- 在大括號中用逗號分隔列出各個選項(常數)。
- 慣例使用 UPPER_SNAKE_CASE(全部大寫,單字以底線分隔)。
enum 可以在哪裡宣告?
- 在獨立檔案(DayOfWeek.java)。
- 在類別內(若只想讓該類別私有使用的列舉)。
- 甚至在方法內(但較少這樣做)。
2. 在程式中使用 enum
宣告 enum 變數:
DayOfWeek today = DayOfWeek.MONDAY;
現在 today 只能取在 DayOfWeek 中宣告的那些值之一。
在 switch 中的使用範例
switch (today)
{
case MONDAY:
System.out.println("唉,星期一……");
break;
case FRIDAY:
System.out.println("耶,星期五!");
break;
default:
System.out.println("普通的一天。");
}
重要差異:在 case 中只寫常數名稱(不需要前綴),因為編譯器已知道變數型別。
遍歷 enum 的所有值
每個 enum 都有靜態方法 values(),會回傳其所有值的陣列:
for (DayOfWeek day : DayOfWeek.values())
{
System.out.println(day);
}
這對於輸出選單、檢查、產生選項等等情境很實用。
3. enum 的方法:name()、ordinal()、valueOf()
Java 的列舉不只是常數清單,而是具方法的完整類別!
方法 name()
回傳常數名稱字串(與原始碼中寫的一樣):
DayOfWeek day = DayOfWeek.FRIDAY;
System.out.println(day.name()); // "FRIDAY"
方法 ordinal()
回傳常數的序號(從 0 開始):
System.out.println(DayOfWeek.MONDAY.ordinal()); // 0
System.out.println(DayOfWeek.FRIDAY.ordinal()); // 4
注意:若未來可能變更順序,請不要用 ordinal() 來儲存或比較 enum!改用名稱更好。
方法 valueOf(String name)
將字串轉換成對應的 enum 值(若該常數存在):
DayOfWeek day = DayOfWeek.valueOf("MONDAY");
System.out.println(day); // MONDAY
若字串不匹配,會拋出 IllegalArgumentException 例外。
方法 values()
回傳 enum 的所有值陣列(上面已示範過):
DayOfWeek[] days = DayOfWeek.values();
4. 具欄位、建構子與方法的 enum
Java 的列舉可以擴充:加入欄位、建構子、方法!沒錯,它不只是名稱清單,而是迷你類別。
範例:包含在地化名稱的星期
public enum DayOfWeek
{
MONDAY("星期一"),
TUESDAY("星期二"),
WEDNESDAY("星期三"),
THURSDAY("星期四"),
FRIDAY("星期五"),
SATURDAY("星期六"),
SUNDAY("星期日");
private final String russianName;
// 建構子(預設為 private)
DayOfWeek(String russianName)
{
this.russianName = russianName;
}
public String getRussianName()
{
return russianName;
}
}
更詳細的運作原理,等到我們在 27+ 級開始學 OOP 時會再說 :P
現在就可以這樣用:
DayOfWeek day = DayOfWeek.WEDNESDAY;
System.out.println(day.getRussianName()); // "星期三"
小技巧:每個 enum 值都可以有自己的資料(此例是中文名稱)。
使用 enum 的優點
- 型別安全:無法意外地把清單之外的值指定給 enum 變數。
- 可讀性:程式碼自我說明(TrafficLight.RED 而不是 1 或 "red")。
- 易於維護:若要新增或刪除選項,只需在單一處修改。
- 可搭配 switch:enum 非常適合依值進行分支。
- 可擴充:可加入欄位、方法、實作介面。
- 更少錯誤:編譯器會幫助你別漏掉處理分支(尤其在 switch 結構中)。
6. 實作:在應用程式中使用 enum
讓我們替這個小型應用程式加入以 enum 表示的星期支援。
步驟 1:宣告 enum
public enum DayOfWeek
{
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}
步驟 2:在程式中使用
System.out.println("請輸入星期(例如:MONDAY):");
Scanner console = new Scanner(System.in);
String input = console.nextLine().toUpperCase();
DayOfWeek day = DayOfWeek.valueOf(input);
switch (day)
{
case MONDAY:
System.out.println("工作週開始了!");
break;
case FRIDAY:
System.out.println("週末快到了!");
break;
case SATURDAY:
case SUNDAY:
System.out.println("萬歲,週末!");
break;
default:
System.out.println("平常的工作日。");
}
流程說明:
- 使用者輸入星期(例如,"monday" 或 "MONDAY")。
- 將字串轉成大寫(toUpperCase()),避免大小寫差異。
- 透過 valueOf 將字串轉為 enum 值。
- 使用 switch 輸出不同訊息。
- 若輸入無效——捕捉錯誤並顯示警告。
7. 使用 enum 的常見錯誤
錯誤一:用字串或數字取代 enum。 初學者常這麼寫:
int day = 1; // 1 代表星期一?
或
String status = "DELIVERED";
這樣不好:容易拼錯、難以維護。請使用 enum!
錯誤二:以 == 與 .equals() 比較 enum。 在 Java 中,enum 值可用 == 比較——這是安全的,因為在同一個 enum 類別中所有值都是唯一的:
if (day == DayOfWeek.MONDAY) { ... } // OK!
雖然 .equals() 也可行,但更常使用 ==。
錯誤三:為 enum 元素取了不好的名稱。 不要將元素命名為 VALUE1、VALUE2、VALUE3 —— 一個月後誰也看不懂!請使用有意義的名稱。
錯誤四:把 ordinal() 拿來做邏輯判斷。 不要這樣寫:
if (day.ordinal() == 0) { ... } // 別這樣!
元素順序可能會變動,邏輯就會壞掉。請改用名稱!
GO TO FULL VERSION