Java 列舉 (Enum)

列舉是一種特殊的類別,用來定義一組固定的常數。

基本語法

public enum Day {
    MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}

// 使用
Day today = Day.MONDAY;
System.out.println(today);  // MONDAY

在 switch 中使用

Day day = Day.SATURDAY;

switch (day) {
    case MONDAY:
    case TUESDAY:
    case WEDNESDAY:
    case THURSDAY:
    case FRIDAY:
        System.out.println("工作日");
        break;
    case SATURDAY:
    case SUNDAY:
        System.out.println("週末");
        break;
}

列舉方法

Day day = Day.WEDNESDAY;

// name() - 取得名稱
System.out.println(day.name());  // WEDNESDAY

// ordinal() - 取得順序(從 0 開始)
System.out.println(day.ordinal());  // 2

// values() - 取得所有值
for (Day d : Day.values()) {
    System.out.println(d);
}

// valueOf() - 從字串轉換
Day monday = Day.valueOf("MONDAY");

帶有屬性的列舉

public enum Size {
    SMALL("S", 10),
    MEDIUM("M", 15),
    LARGE("L", 20);
    
    private final String abbreviation;
    private final int price;
    
    // 建構子必須是 private
    Size(String abbreviation, int price) {
        this.abbreviation = abbreviation;
        this.price = price;
    }
    
    public String getAbbreviation() {
        return abbreviation;
    }
    
    public int getPrice() {
        return price;
    }
}

// 使用
Size size = Size.MEDIUM;
System.out.println(size.getAbbreviation());  // M
System.out.println(size.getPrice());         // 15

帶有方法的列舉

public enum Operation {
    ADD {
        @Override
        public int apply(int a, int b) {
            return a + b;
        }
    },
    SUBTRACT {
        @Override
        public int apply(int a, int b) {
            return a - b;
        }
    },
    MULTIPLY {
        @Override
        public int apply(int a, int b) {
            return a * b;
        }
    },
    DIVIDE {
        @Override
        public int apply(int a, int b) {
            return a / b;
        }
    };
    
    public abstract int apply(int a, int b);
}

// 使用
int result = Operation.ADD.apply(5, 3);  // 8

實作介面

public interface Printable {
    void print();
}

public enum Status implements Printable {
    ACTIVE {
        @Override
        public void print() {
            System.out.println("狀態:啟用");
        }
    },
    INACTIVE {
        @Override
        public void print() {
            System.out.println("狀態:停用");
        }
    }
}

實際應用

HTTP 狀態碼

public enum HttpStatus {
    OK(200, "OK"),
    CREATED(201, "Created"),
    BAD_REQUEST(400, "Bad Request"),
    NOT_FOUND(404, "Not Found"),
    INTERNAL_SERVER_ERROR(500, "Internal Server Error");
    
    private final int code;
    private final String message;
    
    HttpStatus(int code, String message) {
        this.code = code;
        this.message = message;
    }
    
    public int getCode() {
        return code;
    }
    
    public String getMessage() {
        return message;
    }
    
    public static HttpStatus fromCode(int code) {
        for (HttpStatus status : values()) {
            if (status.code == code) {
                return status;
            }
        }
        throw new IllegalArgumentException("Unknown code: " + code);
    }
}

訂單狀態

public enum OrderStatus {
    PENDING("待處理"),
    PROCESSING("處理中"),
    SHIPPED("已出貨"),
    DELIVERED("已送達"),
    CANCELLED("已取消");
    
    private final String description;
    
    OrderStatus(String description) {
        this.description = description;
    }
    
    public String getDescription() {
        return description;
    }
    
    // 檢查是否可以取消
    public boolean canCancel() {
        return this == PENDING || this == PROCESSING;
    }
}

季節

public enum Season {
    SPRING, SUMMER, FALL, WINTER;
    
    public Season next() {
        return values()[(ordinal() + 1) % 4];
    }
}

EnumSet 和 EnumMap

import java.util.EnumSet;
import java.util.EnumMap;

// EnumSet:列舉的高效 Set
EnumSet<Day> weekend = EnumSet.of(Day.SATURDAY, Day.SUNDAY);
EnumSet<Day> weekdays = EnumSet.range(Day.MONDAY, Day.FRIDAY);

// EnumMap:以列舉為 key 的 Map
EnumMap<Day, String> schedule = new EnumMap<>(Day.class);
schedule.put(Day.MONDAY, "會議");
schedule.put(Day.FRIDAY, "發布");

列舉 vs 常數

// 不好:使用常數
public static final int STATUS_ACTIVE = 1;
public static final int STATUS_INACTIVE = 0;
// 問題:沒有型別安全,可以傳入任何 int

// 好:使用列舉
public enum Status { ACTIVE, INACTIVE }
// 優點:型別安全、可讀性好、有方法

注意事項

  • 列舉是類別,隱式繼承 java.lang.Enum
  • 不能繼承其他類別
  • 可以實作介面
  • 列舉是 final 的,不能被繼承
  • 列舉的實例是單例的