《JAVA編程思想》學(xué)習(xí)筆記:第19章(枚舉)

第十九章、枚舉類型

概述:

關(guān)鍵字enum:可以將一組具名的值的有限集合創(chuàng)建為一種新的類型(Class),而這些具名的值可以作為常規(guī)的程序組件使用。

創(chuàng)建enum時,編譯器會為你自動生成一個相關(guān)類,此類自動extends java.lang.Enum類。

1. 基本enum特性

Enum類提供的功能如下:

values() 返回enum實(shí)例的數(shù)組,而且保持聲明的順序:

ordinal() 返回一個int值,這是每個enum實(shí)例在聲明時的次序,從0開始。

== 來比較enum實(shí)例,編譯器會自動提供equals和hashCode方法。

getDeclaringClass() 獲取其所屬的enum類。

name() 返回enum實(shí)例聲明時的名字,與使用toString()效果相同。

Enum.valueOf() 是在Enum中定義的static方法,他根據(jù)給定的名字返回相應(yīng)的enum實(shí)例,如果不存在會拋出異常。

1.1 將靜態(tài)導(dǎo)入用于enum

使用static import能夠?qū)num實(shí)例的標(biāo)識符代入當(dāng)前的命名空間,所以無需再用enum類型來修飾enum實(shí)例。

唯一擔(dān)心的是使用靜態(tài)導(dǎo)入會不會導(dǎo)致代碼令人難以理解。

import static enumerated.Spiciness*;

1.2 向enum中添加新方法

除了不能繼承自一個enum之外,基本上可以將enum看做一個常規(guī)類。

也就是說,可以添加方法,甚至可以有main方法。

必須先定義enum實(shí)例,如果在實(shí)例之前定義任何方法或?qū)傩?,編譯時會報錯。

一旦enum的定義結(jié)束,編譯器就不允許在使用其構(gòu)造器來創(chuàng)建任何實(shí)例了。

public enum OzWitch {

? ? // Instances must be defined first, before methods:

? ? WEST("Miss Gulch"),

? ? NORTH("Glinda"),

? ? EAST("Wicked"),

? ? SOUTH("Good");//必須在enum實(shí)例序列的最后添加一個分號。

? ? private String description;

? ? // Constructor must be package or private access:

? ? private OzWitch(String description) {

? ? ? ? this.description = description;

? ? }

? ? public String getDescription() {

? ? ? ? return description;

? ? }

? ? @Override

? ? public String toString() {}

? ? public static void main(String[] args) {

? ? ? ? for (OzWitch witch : OzWitch.values()) {

? ? ? ? ? ? print(witch + ": " + witch.getDescription());

? ? ? ? }

? ? }

}

2. switch語句中的enum

一般來說switch中只能使用整形值;

而枚舉類型天生就具備整形值的次序,并且可以通過ordinal()方法獲取其次序。

enum Signal {

? ? GREEN, YELLOW, RED,

}

public class TrafficLight {

? ? Signal color = Signal.RED;

? ? public void change() {

? ? ? ? switch (color) {

? ? ? ? ? ? case RED:

? ? ? ? ? ? case GREEN:

? ? ? ? ? ? case YELLOW:

? ? ? ? }

? ? }

}

3.values()的神秘之處

enum類都自動繼承自Enum類(編譯器實(shí)現(xiàn)),我們可以查看Enum中并沒有values()方法。利用反射機(jī)制查看究竟:

values()是由編譯器自動添加的static方法。同時創(chuàng)建Explore的過程中,編譯器還添加了valueOf()方法。不是Enum類已經(jīng)有valueOf()方法了嗎,不過Enum中的ValueOf()方法需要兩個參數(shù),這個新增方法只需一個參數(shù)。由于Set只存儲方法的名字,不考慮簽名,所以removeAll只剩下values。

由于values()方法有編譯器插入到enum定義中的static方法,所以enum向上轉(zhuǎn)型為Enum,那么values就不可訪問了,不過Class中有一個getEnumConstants方法,所以即便Enum接口中沒有vlaues方法,仍然可以通過Class對象取得所有enum實(shí)例:

enum Search {HITHER, YON}

public class UpcastEnum {

? ? public static void main(String[] args) {

? ? ? ? Search[] vals = Search.values();

? ? ? ? Enum e = Search.HITHER; // Upcast

? ? ? ? // e.values(); // No values() in Enum

? ? ? ? for (Enum en : e.getClass().getEnumConstants()) {

? ? ? ? ? ? System.out.println(en);

? ? ? ? }

? ? }

}

getEnumConstants() 獲取所有Enum對象的實(shí)例

5. 實(shí)現(xiàn),而非繼承

不能extends:Java不支持多重繼承;并編譯器自動extends了Enum類了;

可以implements:創(chuàng)建一個新的enum,可以同時實(shí)現(xiàn)一個或多個接口。

enum CartoonCharacter implements Generator<CartoonCharacter> {

? ? SLAPPY, SPANKY, PUNCHY, SILLY, BOUNCY, NUTTY, BOB;

? ? private Random rand = new Random(47);

? ? @Override

? ? public CartoonCharacter next() {

? ? ? ? return values()[rand.nextInt(values().length)];

? ? }

}

6. 隨機(jī)選取

7. 使用接口組織枚舉

子類化:implements是enum 子類化的唯一方法。

有時希望使用子類將一個enum中的元素進(jìn)行分組。在一個接口內(nèi)部創(chuàng)建實(shí)現(xiàn)該接口的枚舉,以此將元素分組。

public interface Food {

? enum Appetizer implements Food {

? ? SALAD, SOUP, SPRING_ROLLS;

? }

? enum MainCourse implements Food {

? ? LASAGNE, BURRITO, PAD_THAI,

? ? LENTILS, HUMMOUS, VINDALOO;

? }

? enum Dessert implements Food {

? ? TIRAMISU, GELATO, BLACK_FOREST_CAKE,

? ? FRUIT, CREME_CARAMEL;

? }

? enum Coffee implements Food {

? ? BLACK_COFFEE, DECAF_COFFEE, ESPRESSO,

? ? LATTE, CAPPUCCINO, TEA, HERB_TEA;

? }

}

public class TypeOfFood {

? public static void main(String[] args) {

? ? Food food = Appetizer.SALAD;

? ? food = MainCourse.LASAGNE;

? ? food = Dessert.GELATO;

? ? food = Coffee.CAPPUCCINO;

? }

}

8. 使用EnumSet替代位標(biāo)志(BitSet)

Set:是一種集合不能添加重復(fù)元素。enum也要求其成員是唯一的。

EnumSet:Java SE5引入了EnumSet,是為了通過enum創(chuàng)建一種替代品,以替代傳統(tǒng)的基于int的“位標(biāo)志”(BitSet)。這種標(biāo)志可以用來表示某種開關(guān)信息,不過,使用這種標(biāo)志,最終操作的只是一些bit。

EnumSet的性能:它在說明一個二進(jìn)制位是否存在時,具有更好的表達(dá)能力,并且無需擔(dān)心性能。

EnumSet 包含的使用方法:

EnumSet.noneOf(AlarmPoints.class); 返回Empty set

EnumSet.complementOf() 用于創(chuàng)建包含與指定的Enum_Set類型相同的元素的EnumSet

EnumSet.of() 返回參數(shù)中添加的Enum元素集合

EnumSet.range() 返回參數(shù)中指定范圍的Enum元素

points.addAll() 添加所有Enum元素

points.removeAl() 移除參數(shù)中包含的Enum元素集合

研究EnumSet文檔,會發(fā)現(xiàn)of()方法被重載了很多次,不但為可變數(shù)量參數(shù)進(jìn)行了重載,而且為接受2至5個顯式的參數(shù)的情況都進(jìn)行了重載。這也從側(cè)面表現(xiàn)了EnumSet對性能的關(guān)注。

9. 使用EnumMap

EnumMap:是一種特殊的Map(Key-Value映射表),要求其中的鍵(key)必須來自于一個enum。

EnumMap的性能:由于enum本身的限制,所以EnumMap內(nèi)部是數(shù)組實(shí)現(xiàn)。因此EnumMap速度很快,可以放心進(jìn)行查找操作。

EnumMap的排序:與EnumSet一樣,enum實(shí)例(key)定義時的次序決定了其在EnumMap中的順序。

9.1 命令模式(GoF23之一)的具體實(shí)現(xiàn)

interface Command {

? ? void action();

}

enum AlarmPoints{

? ? STAIR,LOBBY,OFFICE,BATHROOM,UTILITY,KITCHEN

}

public class EnumMaps {

? ? public static void main(String[] args) {

? ? ? ? EnumMap<AlarmPoints, Command> em =

? ? ? ? ? ? new EnumMap<AlarmPoints, Command>(AlarmPoints.class);

? ? ? ? em.put(KITCHEN, new Command() {

? ? ? ? ? ? public void action() {

? ? ? ? ? ? ? ? print("Kitchen fire!");

? ? ? ? ? ? }

? ? ? ? });

? ? ? ? em.put(BATHROOM, new Command() {

? ? ? ? ? ? public void action() {

? ? ? ? ? ? ? ? print("Bathroom alert!");

? ? ? ? ? ? }

? ? ? ? });

? ? ? ? for (Map.Entry<AlarmPoints, Command> e : em.entrySet()) {

? ? ? ? ? ? printnb(e.getKey() + ": ");

? ? ? ? ? ? e.getValue().action();

? ? ? ? }

? ? ? ? try { // If there's no value for a particular key:

? ? ? ? ? ? em.get(UTILITY).action();

? ? ? ? } catch (Exception e) {

? ? ? ? ? ? print(e);

? ? ? ? }

? ? }

}

9.2 EnumMap 包含的使用方法:

put(k,v) 新增元素(k,v)

get(k) 返回參數(shù)k對應(yīng)的value

entrySet() 返回<k,v>實(shí)例對象集合

Map.Entry<K,V>.getValue() 返回實(shí)例元素的value

10 常量相關(guān)的方法

常量相關(guān)的方法:constant-specific methods

多路分發(fā):multiple dispatching

枚舉元素:在Enum中定義的元素都是Enum類中的各個實(shí)例對象,每個Enum元素都是一個Enum類型的staic final類型對象。

常量相關(guān)的方法的enum實(shí)現(xiàn)方式:enum 允許為每個enum實(shí)例(枚舉元素)編寫方法,從而為每個enum實(shí)例賦予各自不同的行為,從而為每個enum實(shí)例賦予各自不同的行為。

public enum ConstantSpecificMethod {

? ? DATE_TIME {

? ? ? ? String getInfo() {

? ? ? ? ? ? return? DateFormat.getDateInstance().format(new Date());

? ? ? ? }

? ? },

? ? CLASSPATH {

? ? ? ? String getInfo() {

? ? ? ? ? ? return System.getenv("CLASSPATH");

? ? ? ? }

? ? },

? ? VERSION {

? ? ? ? String getInfo() {

? ? ? ? ? ? return System.getProperty("java.version");

? ? ? ? }

? ? };

? ? abstract String getInfo();

? ? public static void main(String[] args) {

? ? ? ? for (ConstantSpecificMethod csm : values()) {

? ? ? ? ? ? System.out.println(csm.getInfo());

? ? ? ? }

? ? }

}

10.1 使用enum的職責(zé)鏈

職責(zé)鏈模式:(Chain of Responsibility,GoF23設(shè)計模式之一):程序員以多種不同的方式來解決一個問題,然后將它們鏈接在一起。當(dāng)請求到來時,它遍歷這個鏈,直到這個鏈中的某個解決方案能夠處理此需求。

代碼示例 參照原文P607

10.2 使用enum的狀態(tài)機(jī)

狀態(tài)機(jī)模式:(GoF23設(shè)計模式之一):一個狀態(tài)機(jī)可以具有有限個特定的狀態(tài),它通常根據(jù)輸入,從一個狀態(tài)轉(zhuǎn)移到下個狀態(tài),不過也可能存在瞬時狀態(tài)(transient states),而一旦任務(wù)執(zhí)行結(jié)束,狀態(tài)機(jī)會立即離開瞬時狀態(tài)。

瞬時狀態(tài):(transient states):

代碼示例 參照原文P609

11 多路分發(fā)

單路分發(fā):Java只支持單路分發(fā):如果要執(zhí)行的操作包含了不只一個類型未知的對象時,java的動態(tài)綁定機(jī)制只能處理其中一個的類型。

多路分發(fā):含二路分發(fā)。

多路分發(fā)的幾種實(shí)現(xiàn)方式:

使用enum分發(fā):

使用常量相關(guān)的方法分發(fā)

使用EnumMap分發(fā)

使用二維數(shù)組

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容