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 java第4章 類和介面
第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 Java》第4章 類和介面
優先考慮靜態成員類【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