首頁>技術>

用enum代替int常量

1. int列舉:

引入列舉前,一般是宣告一組具名的int常量,每個常量代表一個型別成員,這種方法叫做int列舉模式。

int列舉模式是型別不安全的,例如下面兩組常量:性別和動物種類,二者不存在任何關係,然而卻可以將ANIMAL_DOG傳入一個需要性別引數的方法中,編譯器不會出現警告,而且方法內部邏輯還會繼續執行比較等操作。

採用int列舉模式的程式是十分脆弱的,因為int列舉是編譯時常量,被編譯到使用它們的客戶端中,如果與列舉常量關聯的int值發生變化客戶端就必須重新編譯,然而不重新編譯卻不會報錯,但是會導致程式的結果不準確,例如上面的常量SEX_MAN被客戶端使用,於是將其值1編譯到客戶端的.class中,然後如果API類中將SEX_MAN的值改為2,卻不重新編譯客戶端,那麼客戶端得到的結果就是不準確的了。

int列舉常量很難被翻譯成可列印的字串,不利於開發除錯。

開發過程中還可能遇到這種模式的變體,如String列舉模式,同樣是存在上述問題。

2. 列舉型別:

由一組固定的常量組成的合法值的型別,例如:

實現思路:透過公有靜態的final域為每個列舉常量匯出例項的類。

列舉型別是型別安全的列舉模式,而且完善類int列舉模式的不足。

列舉型別還允許新增任意的方法和域,並實現任意的介面,它提供類所以Object方法的高階實現,實現了Comparable和Serializable介面,並針對列舉型別的可任意改變性設計了序列化方式。

特定於常量的方法實現:在列舉型別中宣告一個抽象方法,並在特定於常量的類主體中,用具體的方法覆蓋每個常量的抽象方法。例如下面這樣定義一個代表加減乘除等運算子的列舉。

策略列舉:多個列舉常量同時共享相同的行為時,考慮使用策略列舉。例如下面這樣:

用例項域代替序數

序數:列舉天生就與一個單獨的int值相關聯,所有列舉都有一個ordinal()方法,返回每個列舉常量在型別中的數字位置(類似於陣列索引)。

永遠不要根據列舉的序數匯出與他相關聯的值,而是將它儲存在一個例項域中 (Enum規範中關於ordinal()寫到:"大多數程式設計師都不需要這個方法,它是設計成用於像EnumSet,EnumMap這種基於列舉的通用資料結構的)。

用EnumSet代替位域

位域:可以用or(|)位運算將幾個常量合併到一個集合中,例如下面程式碼這樣:

位域的不足:具有int列舉的所有缺點

替代方案--EnumSet:從單個列舉型別中提取多個值,每個EnumSet內容都表示為位向量, 如果底層的列舉型別有64或更少的元素(大多如此),整個EnumSet就是用單個long來表示, 因此,它的效能比得上位域的效能

用EnumMap代替序數索引

使用序數索引ordinal的場景:

上面程式碼實現了對花園中對植物進行分類,然而存在許多問題:

1.陣列不能與泛型相容,需要進行未受檢對轉換;2.set陣列並不知道每個索引set的set代表什麼;3. 之前有提到不推薦使用ordinal;

解決方案:EnumMap

用介面模擬可伸縮的列舉:

雖然無法編寫可擴充套件的列舉型別,卻可以透過編寫介面以及實現該介面的基礎列舉型別,對它進行模擬,這樣允許客戶端編寫自己的列舉來實現介面;如果API是根據介面編寫的,那麼在使用基礎列舉型別的任何地方,也都可以使用這些列舉。例如下面程式碼,還是用之前的算數運算子舉例:(但是這樣還是有些不足,就是無法實現從一個列舉型別繼承到另一個列舉型別,程式碼少的當然可以直接複製貼上, 如果功能比較多則可以將他們封裝在一個輔助類或靜態輔助方法中,避免程式碼的複製工作)。

註解優先於命名模式

- 命名模式:有些程式元素需要透過某種工具或框架進行特殊處理

例1:JUnit測試框架原本要求使用者一定要用test作為測試方法名的開頭

例2:iOS中的init方法要求必須是initXXX()

- 命名模式缺陷:

1. 文字拼寫錯誤會導致失敗,且沒有任何提示,造成錯誤的安全感,如JUnit的測試方法testXX寫成textXX或tsetXX等

2. 無法確保他們只用於相應的程式元素,如JUnit的命名只對方法生效,將某個類命名testXX是無效的,不會報錯,但不會執行測試

3. 沒有提供將引數值與程式元素關聯起來的好方法,如JUnit想增加一種測試類別,只在丟擲某種特定異常時才會成功, 而這個異常型別需要使用者透過引數進行自定義,這種實現透過命名模式實現(將異常型別編寫到方法名中)並不理想。

註解對上面問題的解決,請看下面程式碼:

堅持使用Override註解

應該在想要覆蓋超類宣告的每個方法宣告中使用Override註解 例如我們經常會重寫自定義模型類的equals方法,下面用程式碼說明使用Override註解的優勢。

使用Override還有一點好處,就是可以區分哪些方法是超類對,哪些方法子類擴充套件對

用標記介面定義型別

標記介面:沒有方法宣告,只是表示具有某種屬性,如Serializable介面

標記介面的優點 :

1. 標記介面定義的型別是由被標記類的例項實現的,標記註解則沒有這樣的型別2. 標記介面可以更加精確的被鎖定,可以是對其他介面的擴充套件,也可以被其他標記介面擴充套件,如Collection和Set

標記註解的優點:1. 可以透過預設方式新增一個或多個註解型別的元素,給已被使用的註解型別新增更多的資訊,方便擴充套件2. 另一個優點在於它們是更大的註解機制的一部分,因此,標記註解,在那些支援註解作為程式設計元素之一的框架中同樣具有一致性

如何選擇?

- 如果標記是應用到任何程式元素而不只是類或介面,那就必須使用註解- 如果只是用於類或介面,需要考慮要編寫只接受有這種標記的方法,使用介面作為相關方法的引數型別, 可以提供編譯時就進行型別檢查的好處- 是否要永遠限制這個標記只用於特殊介面的元素,如果是,最好將標記定義成該介面的一個子介面

18
最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • 多功能SQL資料庫編輯器v9.3.0永久啟用版