首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

《Thinking in Java》筆記(19):枚舉 類型進階特性與設計模式應用

     編輯:Java 相關知識

Java 5 引入 enum,不僅是類型安全的常量集合,更是功能豐富的類:可實現接口、定義方法、字段、構造器、實現狀態機、策略模式、責任鏈、多重分派等。本文系統梳理枚舉核心特性、values() 機制、EnumSet/EnumMap 高性能集合、常量特定方法、狀態機與分派模式實戰。

一、枚舉基礎特性

  • 編譯器生成繼承 java.lang.Enum 的 final 類,不可被繼承。
  • ordinal() 返回聲明順序(從 0 開始),name() 返回常量名。
  • 實現 Serializable、Comparable(按 ordinal 排序)。

二、向枚舉添加方法與字段


public enum Operation {
    PLUS("+") { public double apply(double x, double y) { return x + y; } },
    MINUS("-") { public double apply(double x, double y) { return x - y; } },
    TIMES("*") { public double apply(double x, double y) { return x * y; } },
    DIVIDE("/") { public double apply(double x, double y) { return x / y; } };

    private final String symbol;
    Operation(String symbol) { this.symbol = symbol; }
    public String getSymbol() { return symbol; }
    public abstract double apply(double x, double y); // 抽象方法強制每個常量實現
    
    // 靜態工廠方法:從符號反查
    private static final Map BY_SYMBOL = 
        Arrays.stream(values()).collect(toMap(Operation::getSymbol, o -> o));
    public static Operation fromSymbol(String s) { return BY_SYMBOL.get(s); }
}
  • 常量必須在最前聲明,以分號結束常量列表。
  • 構造器默認 private,只能在枚舉內部調用。
  • 可覆寫 toString() 自定義顯示。

三、switch 語句中的枚舉


public void execute(Operation op, double x, double y) {
    switch (op) { // case 中無需寫 Operation.PLUS
        case PLUS: System.out.println(x + y); break;
        case MINUS: System.out.println(x - y); break;
        default: throw new IllegalArgumentException("Unknown op: " + op);
    }
}
  • 編譯器基於 ordinal() 實現 tableswitch/lookupswitch,效率極高。
  • 若所有 case 都 return,編譯器要求有 default(防止未來新增常量漏處理)。

四、values() 方法之謎

  • values() 是編譯器插入的 static 方法,不在 Enum 父類中。
  • 向上轉型為 Enum 後將丟失 values(),需通過 Class.getEnumConstants() 獲取。
  • 編譯器生成的枚舉類被標記 final,不可繼承。

五、實現接口而非繼承


public interface Food { enum Category { MEAT, VEGETABLE } }
public enum Meat implements Food.Category { BEEF, PORK, CHICKEN }
public enum Vegetable implements Food.Category { CARROT, POTATO, TOMATO }
  • 枚舉隱式繼承 Enum,Java 不支持多重繼承,故不可繼承其他類。
  • 可實現多個接口,實現「分類」與「類型安全」雙重保障。

六、EnumSet:位向量高性能集合

  • 內部用 long (≤64 元素) 或 long[] 位向量存儲,極其緊湊高效。
  • 所有操作 O(1),無裝箱開銷,替代傳統 int 位掩碼。
  • 工廠方法:EnumSet.allOf()、EnumSet.noneOf()、EnumSet.of()、EnumSet.range()、EnumSet.complementOf()。

七、EnumMap:枚舉專用 Map

  • 鍵必須為同一枚舉類型,內部用數組實現,順序與枚舉聲明順序一致。
  • 比 HashMap 更快、更省內存,無哈希碰撞。
  • 值可變,適合運行期動態配置(常量特定方法為編譯期固定)。

八、常量特定方法:實現多態行為


public enum PayChannel {
    ALIPAY {
        @Override public void pay(double amt) { System.out.println("支付寶支付 " + amt); }
        @Override public String getName() { return "支付寶"; }
    },
    WECHAT {
        @Override public void pay(double amt) { System.out.println("微信支付 " + amt); }
        @Override public String getName() { return "微信"; }
    };
    public abstract void pay(double amt);
    public abstract String getName();
}
  • 每個常量都是獨立匿名內部類實例,可覆寫抽象方法實現差異化行為。
  • 替代大量 if-else/switch,符合開閉原則。

九、枚舉實現狀態機


public enum State {
    IDLE {
        @Override State next(Event e) {
            return e == Event.START ? RUNNING : IDLE;
        }
    },
    RUNNING {
        @Override State next(Event e) {
            return e == Event.PAUSE ? PAUSED : e == Event.STOP ? IDLE : RUNNING;
        }
    },
    PAUSED {
        @Override State next(Event e) {
            return e == Event.RESUME ? RUNNING : e == Event.STOP ? IDLE : PAUSED;
        }
    };
    public abstract State next(Event e);
}

public enum Event { START, PAUSE, RESUME, STOP }

// 使用
State s = State.IDLE;
s = s.next(Event.START); // RUNNING
  • 枚舉天然限定狀態集合,編譯期保證窮盡性。
  • 避免 if-else 散落業務代碼,狀態轉移邏輯集中在枚舉內部。

十、多重分派與枚舉

Java 單分派(只根據 this 動態綁定),雙分派需兩次方法調用。枚舉輔助三種實現:

  1. 常量特定方法:enumA.execute(enumB) 內部 switch(enumB),雙分派退化為單分派+switch。
  2. EnumMap 雙分派:Map, Action> 查表,O(1) 查找,真正雙分派。
  3. 二維數組:Action[][] table,最簡潔最高效,但類型安全稍弱。

// EnumMap 雙分派示例
enum Shape { CIRCLE, RECT }
enum Color { RED, BLUE }

interface DrawAction { void draw(); }

EnumMap> table = new EnumMap<>(Shape.class);
for (Shape s : Shape.values()) {
    EnumMap row = new EnumMap<>(Color.class);
    for (Color c : Color.values()) row.put(c, () -> System.out.println(s + "-" + c));
    table.put(s, row);
}

// 調用
table.get(Shape.CIRCLE).get(Color.RED).draw();
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved