1. 程式人生 > >effective java 第6章 列舉和註解 第30條 用enum代替int常量

effective java 第6章 列舉和註解 第30條 用enum代替int常量

列舉是什麼?

       列舉型別是指由一組固定的常量組成合法值的型別。列舉型別是解決常量int的另一種解決方案。

列舉型別的簡單示例:

public enum AlarmPoints {STAIR1,STAIR2,LOBBY,OFFICE1,OFFICE2,OFFICE3,OFFICE4,BATHROOM,UTILITY,KITCHEN}

列舉的優勢:

     列舉型別是真正的final.所以客戶端即不能建立,也不能對它進行擴充套件,因此很可能沒有例項,而只有申明過的列舉常量。換句話說,列舉型別是例項受控的。它們是單例的泛型化。

     列舉型別提供檢查異常。

     命名直觀,可讀性強。

     實現了Comparable和Serialicable 介面。

簡單列舉型別對於大多數列舉型別來說,已經夠用了,但有的時候你可能需要更多的方法。有的時候,你需要將本質不同的行為和每個常量聯絡起來。比如,編寫4中計算方式的列舉型別:

public enum Operation {
    PLUS,
    MIMUS,
    TIMES,
    DIVIDE;
    double apply(double x,double y){

        switch(this){
            case PLUS: return x + y;
            case MIMUS: return x - y;
            case TIMES: return x * y;
            case DIVIDE:return x / y;
        }
        throw new AssertionError("Unknown op:" + this);
    }
}

這段程式碼看似可行,只是不太好看,如果沒有throw語句,就沒有辦法編譯通過,但是實際上不可能知道最後的語句。最糟糕的是,如果你添加了新的列舉型別,而switch中卻沒有新增,編譯異常通過,執行卻會出錯。有一種解決方案:

public enum Operation {
    PLUS{
        @Override
        double apply(double x, double y) {
            return  x + y;
        }
    },
    MIMUS {
        @Override
        double apply(double x, double y) {
            return x - y;
        }
    },
    TIMES {
        @Override
        double apply(double x, double y) {
            return x * y;
        }
    },
    DIVIDE {
        @Override
        double apply(double x, double y) {
            return x / y;
        }
    };
    abstract double apply(double x,double y);
}

在這中寫法下,你新增新常量,編譯器會提醒你重寫抽象方法,你也必須重寫抽象方法;

它可以和特定的符號結合起來:

public enum Operation {
    PLUS("+"){
        @Override
        double apply(double x, double y) {
            return  x + y;
        }
    },
    MIMUS ("-"){
        @Override
        double apply(double x, double y) {
            return x - y;
        }
    },
    TIMES ("*"){
        @Override
        double apply(double x, double y) {
            return x * y;
        }
    },
    DIVIDE("/") {
        @Override
        double apply(double x, double y) {
            return x / y;
        }
    };
    private final String sysmbol;

    Operation(String sysmbol){
        this.sysmbol = sysmbol;
    }

    @Override
    public String toString() {
        return sysmbol;
    }

    abstract double apply(double x, double y);
}
    public static void main(String[] args) {
        double x = 2.0;
        double y = 4.0;
        Arrays.asList(values()).forEach(o->{
            System.out.printf("%f %s %f = %f%n",x,o,y,o.apply(x,y));
        });
    }

2.000000 + 4.000000 = 6.000000
2.000000 - 4.000000 = -2.000000
2.000000 * 4.000000 = 8.000000
2.000000 / 4.000000 = 0.500000

打印出了很工整的表單。

列舉型別有一個valueOf(String name)方法,可以將字串轉換成真是列舉型別,由於覆蓋了toString 方法,所以編寫一個fromString方法;

    private static final Map<String,Operation> stringToEnum = new HashMap<>();
    static {
        Arrays.asList(Operation.values()).forEach(o-> stringToEnum.put(o.toString(),o));
    }

    public static Operation fromString(String symbol){
        return stringToEnum.get(symbol);
    }

特定於常量的方法實現中有一個美中不足的地方,它們使得列舉常量中共享程式碼變得更加困難了。比如,考慮用一個列舉表示薪資包中的工作天數。這個列舉中有一個方法,根據給定某工人的基本工資以及當天的工作時間,來計算它當天的報酬。在五個工作日中,超過8個小時算加班,加班有額外工資,週六,周天,全天算加班。

public enum PayrollDay {
    MON,TUE,WED,THU,FRI,SAT,SUN;
    private static final int HOUTS_PER_SHIFT = 8;

    public double get(final int hours,double payRate){
        double base = hours * payRate;
        double over;
        switch (this){
            case SAT:case SUN:
                over = hours * payRate * 0.5;
                default: over = hours >= HOUTS_PER_SHIFT ?
                        (hours - HOUTS_PER_SHIFT) * payRate * 0.5: 0;
        }
        return base + over;
    }
}

程式碼非常簡潔,但是確難以維護,如果某人有一個工作日,確實放假,但是又忘記新增switch,程式依然可以執行,只是結果不正確而已。

你真正想要的就是每當新增一個列舉常量是,就強制選擇一種加班報酬策略,又一種很好的方法可以實現這一點,新增一個列舉策略,來實現對加班工資的計算,雖然這種方式沒有那麼簡潔,但卻更加安全,也更加靈活:

public enum PayrollDay {
    MON(PayType.WEEKDAY),TUE(PayType.WEEKDAY),WED(PayType.WEEKDAY),
    THU(PayType.WEEKDAY),FRI(PayType.WEEKDAY),
    SAT(PayType.WEEKEND),SUN(PayType.WEEKEND);
    //支付方式
    private final PayType payType;
    //初始化payType
    PayrollDay(PayType payType){
        this.payType = payType;
    }
    //計算
    public double pay(final int hours,double payRate){
        return payType.get(hours,payRate);
    }


    enum PayType{
        WEEKDAY {
            @Override
            double overGet(int hours, double payRate) {
                return hours >= HOUTS_PER_SHIFT ?
                        (hours - HOUTS_PER_SHIFT) * payRate * 0.5: 0;
            }
        },
       WEEKEND {
           @Override
           double overGet(int hours, double payRate) {
               return  hours * payRate * 0.5;
           }
       };
       private static final int HOUTS_PER_SHIFT = 8;

       abstract double overGet(final int hours,double payRate);
        public double get(final int hours,double payRate){
            double base = hours * payRate;
            double over = overGet(hours,payRate);
            return base + over;
        }
   }
}

測試:

    public static void main(String[] args) {
        double s = SUN.pay(10,10);
        double s1 = MON.pay(10,10);
        double s2 = MON.pay(7,10);
        System.out.println(s + "..." + s1 + "..." + s2);
    }

150.0...110.0...70.0

列舉中的switch語句適合於給外部的列舉型別增加特定於常量的行為,或改變,或替換

public class Demo{
    public static Operation inverse(Operation o){
        switch (o){
            case PLUS: return MIMUS;
            case MIMUS:return PLUS;
            case TIMES:return DIVIDE;
            case DIVIDE:return TIMES;
            default: throw new AssertionError("Unknow o:" + o);
        }
    }
}

這裡使用了靜態匯入

相關推薦

effective java 6 列舉註解 30 enum代替int常量

列舉是什麼?        列舉型別是指由一組固定的常量組成合法值的型別。列舉型別是解決常量int的另一種解決方案。 列舉型別的簡單示例: public enum AlarmPoints {STAIR1,STAIR2,LOBBY,OFFICE1,OFFICE2,OFFI

編寫高質量程式碼:改善Java程式的151個建議(6:列舉註解___建議83~87)

列舉和註解都是在Java1.5中引入的,列舉改變了常量的宣告方式,註解耦合了資料和程式碼。 建議83:推薦使用列舉定義常量 常

Effective java筆記- 列舉註解

列舉和註解 第30條 用enum代替int常量 int列舉模式的缺點: 1.無名稱空間,所以要加字首防止名稱衝突 2.int列舉是編譯時常量,一旦常量關聯的int值變化,就要重新編譯 3.沒有很好的列印字串的方法(有一種String列舉常量,但是效能不好

列舉註解

30. 用enum代替int常量 31. 用例項域代替序數 32. 用EnumSet代替位域 EnumSet是執行緒不安全的,需要藉助Collections.synchronizedSe

程式碼整潔之道 讀書筆記 - 6 物件資料結構

資料結構、物件的反對稱性 物件(物件式程式碼)曝露行為,隱藏資料。便於新增新物件型別而無需修改既有行為,同時也難以在既有物件中新增新行為。 資料結構(過程式程式碼)曝露資料,沒有明顯的行為。便於向既有資料結構新增新行為,同時也難以向既有函式新增新資料結構。 在任何系統中,我們有時會希望能夠靈活地新增新資

effective java中文版 第二 建立銷燬物件

第1條:考慮用靜態工廠方法替代構造器 如下方法將boolean基本型別值轉換為了一個Boolean物件引用 public static Boolean valueOf(boolean b){ return b ?Boolean.TRUE:Boolean.FALSE; } 靜態

Netty原始碼分析6(解碼器)---->3節: 行解碼器

  Netty原始碼分析第六章: 解碼器   第三節: 行解碼器   這一小節瞭解下行解碼器LineBasedFrameDecoder, 行解碼器的功能是一個位元組流, 以\r\n或者直接以\n結尾進行解碼, 也就是以換行符為分隔進行解析 同樣, 這個解碼器也繼承了B

Netty原始碼分析6(解碼器)---->2節: 固定長度解碼器

  Netty原始碼分析第六章: 解碼器   第二節: 固定長度解碼器   上一小節我們瞭解到, 解碼器需要繼承ByteToMessageDecoder, 並重寫decode方法, 將解析出來的物件放入集合中集合, ByteToMessageDecoder中可以將解析出

Netty原始碼分析6(解碼器)---->1節: ByteToMessageDecoder

  Netty原始碼分析第六章: 解碼器   概述:           在我們上一個章節遺留過一個問題, 就是如果Server在讀取客戶端的資料的時候, 如果一次讀取不完整, 就觸發channelRead事件, 那麼Netty是

Netty原始碼分析6(解碼器)---->4節: 分隔符解碼器

  Netty原始碼分析第六章: 解碼器   第四節: 分隔符解碼器     基於分隔符解碼器DelimiterBasedFrameDecoder, 是按照指定分隔符進行解碼的解碼器, 通過分隔符, 可以將二進位制流拆分成完整的資料包   同樣

JAVA核心技術筆記總結--6 抽象類、接口、內部類Lambda表達式

重載方法 lam 類變量 cat 而在 訪問 負責 3.4 第一次 6.1 抽象類 抽象類是指定義時有 abstract 修飾的類,例如: public abstract class Person{ ... public abstract String getD

effective java4介面

第13條 使類和成員的可訪問性最小化 第一規則:儘可能地使每個類或成員不被外界訪問 只有當同一個包內的另一個類真正需要訪問一個成員的時候,你才應該刪除private修飾符。 如果方法覆蓋了超類中的一個方法,子類中的訪問級別就不允許低於超類中的訪問級別。這樣可確保任何可使用超類例項的地方也可以

《瘋狂Java講義(4版)》-----6【面向物件(下)】(命令模式、Lambda、列舉類、垃圾回收、jar)

命令模式 命令模式,參考下面程式碼,就能領悟到,想做出怎樣的處理,就怎麼去覆蓋介面中的抽象方法!下面程式碼中介面Command中定義一個對陣列處理的方法,但沒說怎麼處理(畢竟是抽象方法嘛),然後讓其他類來實現這個介面,順便實現這個介面的方法,他們按照何種方式實現

EffectiveJava列舉註解

討論列舉和註解的最佳實踐。 30. 用enum代替int常量 列舉型別(enum type)是指由一組固定的常量組成合法值得型別,在程式語言還沒有引入列舉之前,表示列舉型別的常用模式是宣告一組具名的int常量,稱作int列舉模式。 int列舉模式的不

effective java 2 建立銷燬物件筆記

第一條:考慮用靜態工廠方法替代構造器 靜態工廠方法的優點: 1.具有名字,有可閱讀性。 2.不必在每次呼叫他們的時候都建立一個新物件。 應用:①單例物件 ②列舉 3.可以返回返回值型別的任何子型別的物件。 應用: ①EnumSet的靜態工廠方法根據元素的個數選擇返回Reg

Effective Java4介面

優先考慮靜態成員類【Item 22】 1)巢狀類(nested class)是指被定義在一個類的內部的類,它的目的是為其外圍類(enclosing class)提供服務 2)巢狀類有靜態成員類、非靜態成員類、匿名類、區域性類四種,其中靜態成員類為非內部類,其無異於外圍類,僅僅是位置位於外圍類內部,即使沒有

6 靜態路由動態路由(1)_靜態路由

align 將在 跟蹤 添加 測試 字母 ppp協議 必須 缺少 1. 路由——網絡層實現的功能 1.1 路由功能 (1)網絡層的功能:給傳輸層協議提供簡單靈活的、無連接的、盡最大努力交付的數據包服務。 (2)路由器為每一個數據包單獨地選擇轉發路徑,網絡層並不提供服務質量的

Java編程思想讀書筆記_6(訪問權限)

ack string 屬於 cte pri 包訪問權限 print code int 四種訪問權限: public private 包訪問權限 protected 如果沒有明確指定package,則屬於默認包 1 package access.dessert; 2

Java開發工程師(Web方向) - 01.Java Web開發入門 - 6.蜂巢

多個 計算 margin style 打包 工程 自動 後端服務 運行 第6章--蜂巢 蜂巢簡介 網站開發完,就需要測試、部署、在服務器上運行。 網易蜂巢: 采用Docker容器化技術的雲計算平臺 https://c.163.com 容器管理:容器可被視作為雲主機的服務器

6 數據篩選排序

view 按鈕 窗口 ren 順序 文字 ted 常用屬性 一個 第6章 數據篩選和排序 一. TreeView 控件:樹狀控件,用於以節點形式顯示文本或數據,這些節點按層次結構的順序排列. 1. TreeView控件的常用屬性和事件Nodes Nodes