《Java編程思想》筆記 第十九章 枚舉類型
阿新 • • 發佈:2018-08-21
void serial final eof coff rabl com 筆記 bstr
1.基本enum特征
- 所有創建的枚舉類都繼承自抽象類 java.lang.Enum;
- 一個枚舉類,所有實例都要在第一句寫出以 ,隔開。 如果只有實例最後可以不加 ; 枚舉類因為繼承了Enum,所以再不能繼承別的類,任何類也不能繼承枚舉類(構造器默認為private)。
public enum Color { RED, BLUE, YELLOW, PURPLE }
註意 :RED,BLUE 這些是由 enum Color類調用默認private構造器創建的實例對象,和普通class相比只不過enum的實例對象只能在內部創建。時刻記著他們是一個實例對象。
- 枚舉類實例不能在外部 new 出來 ,因為枚舉類構造器為private 一般也不用聲明private,默認就是private,因為enum實例只能在編譯期間enum類內部被創建,但可以外部得到一個實例的引用。
Color red = Color.RED; Color blue = Color.BLUE;
- 枚舉類的一些方法
- 使用枚舉元素 Color.RED 或者 red
- Color.values( ) 返回一個枚舉類數組,數組元素就是枚舉的實例。
- red.ordinal() 方法返回該實例聲明的次序從0開始。
- 2個實例可用 == 比較
- Enum 實現了 Comparable<E>和 Serializable 可以使用comparableTo( )方法,可以序列化
- a. getDeclaringClass() 返回 Color.class 對象
- a.name和a.toString( )方法一樣。
- valuesOf(string ) 返回一個實例對象 Color b = Color.valueOf("BLUE");
- 根據class對象返回實例 Color b = Color.valueOf( Color.class, "BLUE" )
- 通過帶參構造器為枚舉實例添加描述信息。調用 getDes()就可以的到當前對象的描述信息。
public enum Color { RED("這是紅色"), BLUE("這是藍色"), YELLOW(
調用 getDes( )方法就可以的到當前對象的描述信息。
-
重寫toString( )方法來為enum實例添加描述信息。
public enum Color { RED, BLUE, YELLOW, PURPLE; @Override public String toString() { String id = name(); return id + " is " + id.toLowerCase(); } }
通過name() 拿到當前對象名字。
2.enum的特殊方法。
- 除了不能繼承enum類外,enum和其普通類沒區別,可以添加字段,方法,甚至是main 方法
- enum 實例也可以用在 switch語句中
- values()方法不在Enum中 它是由編譯器添加的static方法。
- 編譯器還添加了一個valuesOf(String s)方法,這個只需要一個參數就可以的得到實例,而Enum的需要2個。
- 如果將enum向上轉型為Enum那麽values 和 valuesOf 無法再使用。
- 與values 方法有相同作用的就是Class對象的getEnumConstants(),如果class是枚舉類那麽返回元素數組,不是枚舉類返回null
3. 使用接口組織枚舉
- 為了實現擴展enum 或者將enum分類,因為無法繼承所以靠擴展子類無法實現,可以利用接口來達到這些功能。
- 接口內部創建實現該接口的enum 達到分類目的,並且可以向上轉型為接口
public interface Food { enum Fruit implements Food{ APPLE, BANANA, ORANGE; } enum Vegetables implements Food{ TOMATO, POTATO, BEANS; } enum Drink implements Food{ COFFEE, COCA, REDBULL; } } public static void main(String[] args) { Food food = Food.Fruit.APPLE; food = Food.Drink.REDBULL; food = Food.Vegetables.BEANS; }
- 接口基礎上創建一個枚舉的枚舉,通過該enum控制其他enum,而不是不同的類型分別都要向上轉型為Food ,類多時分別向上轉型不如每個用一個enum控制方便。
public enum Course { FRUIT(Food.Fruit.class), DRINK(Food.Drink.class), VEGETABLES(Food.Vegetables.class); private Food[] values; Course(Class<? extends Food> kind) { this.values = kind.getEnumConstants(); } public Food[] getValues() { return values; } }
通過實例調用getValues方法就可以的到該實例的所有元素。
- enum嵌套在另一個enum 重新組織1 2 代碼合二為一
public enum Course { FRUIT(Food.Fruit.class), DRINK(Food.Drink.class), VEGETABLES(Food.Vegetables.class); private Food[] values; Course(Class<? extends Food > kind) { this.values = kind.getEnumConstants(); } interface Food { enum Fruit implements Food { APPLE, BANANA, ORANGE; } enum Vegetables implements Food { TOMATO, POTATO, BEANS; } enum Drink implements Food { COFFEE, COCA, REDBULL; } } public Food[] getValues() { return values; } }
View Code
4. EnumSet
- EnumSet (抽象類)一個用來存放enum 元素的Set,存取enum速度非常快,性能非常高。
- EnumSet 只能存放enum元素,不能插入空元素。
- 放入的元素位置和enum中保持一樣,它處於排序狀態,是一個有序Set.
- Enum 是個抽象類且方法除了colon( )克隆一個EnumSet外都是靜態方法返回值都是EnumSet。
EnumSet<Color> enumSet = EnumSet.noneOf(Color.class); //創建一個空集 EnumSet<Color> enumSet2 = EnumSet.allOf(Color.class); //把集合中所有元素添加進去 EnumSet<Color> enumSet3 = EnumSet.of(RED);//添加一個元素
EnumSet不止這幾個方法,對於of() 方法重載了6次,當傳入2-5個參數調用相應方法,傳入1個或者5個以上調用可變參數。
5.EnumMap
- 特殊Map(類),key必須是enum, 由於enum元素有限所以內部只是由數組實現。
- 這是一個有序map,保持enum的元素順序。
- EnumMap<Color,Object> map = new EnumMap<Color, Object>(Color.class);
- 方法和其他Map一樣。
6.實例對象添加方法
- 為每一個enum實例(相當於常量)添加一個方法,讓他們有不同的行為。
- 為每一個實例添加不同行為的方法
- 在enum中創建一個或者多個abstract 方法,因為是enum的實例所以就得實現這些方法。
RED{ @Override String getInfo() { return null; } @Override String getTime() { return null; } }; abstract String getInfo(); abstract String getTime();
- enum也可以有main方法作為enum執行入口
- 常量添加方法後和有了類的行為,貌似和內部類一樣,但他們有著不同行為,enum的常量不能作為方法參數類型,因為他們不是類,只是enum類型的static final 實例
- 由於enum的常量是 static final 的所以常量的方法不能訪問外部類的非靜態方法。
- 在enum中創建一個或者多個abstract 方法,因為是enum的實例所以就得實現這些方法。
- 覆蓋常量相關的方法
- enum中所有非抽象方法每個實例都可以調
- 如果不需要每個實例都實現抽象類,那麽就可以不用定義抽象類,每個實例各自實現方法,實現的方法可以覆蓋enum中的方法。
- 使用enum職責鏈。
- 多種不同的方式解決問題,然後把它們連接在一起,但一個請求到達時遍歷整個鏈,直到解決問題。
- enum非常適合作為解決某一個問題的職責鏈,請求到達時遍歷整個enum,直到解決問題。
import java.util.EnumSet; import java.util.Random; public enum COR { SOLUTION_ONE{ @Override boolean Solve(int i) { if (i == 1){ System.out.println(name()+" 解決問題 " +i); return true; } return false; } }, SOLUTION_TWO{ @Override boolean Solve(int i) { if (i == 2){ System.out.println(name()+" 解決問題 " +i); return true; }return false; } }, SOLUTION_THREE{ @Override boolean Solve(int i) { if (i == 3){ System.out.println(name()+" 解決問題 " +i); return true; }return false; } }, SOLUTION_FOUR{ @Override boolean Solve(int i) { if (i == 4){ System.out.println(name()+" 可以解決問題 " +i); return true; }return false; } }; abstract boolean Solve(int i); public static void main(String[] args) { Random random = new Random(); EnumSet<COR> cors = EnumSet.allOf(COR.class); for (int i = 0; i < 6; i++) { int id = random.nextInt(4)+1; for (COR cor :cors) { if (cor.Solve(id)){ System.out.println(" 解決問題 " +id); break; } } } } }
View Code
- enum狀態機
- 狀態機可以具有 有限個 狀態,通常根據輸入,從一個狀態轉移到下一個狀態,也可以有瞬時狀態,一但任務結束就立刻離開瞬時狀態。
7.多路分發
- 多種類型交互時有時並不能確定所有類型,如: NUM.complete(NUM) , NUM 是所有數字類型的超類,a.complete(b) ,a b可能是同種類型也可能不是同一種類型。
- Java 動態綁定只能處理一種類型,屬於單路分發(分派),動態綁定能將complete綁定到分路a。只有方法調用才會執行動態綁定。
- 可以為每一個分發實現自己的動態綁定
public enum Outcome { WIN, LOSE, DRAW } ///:~ interface Item { Outcome compete(Item it); Outcome eval(Paper p); Outcome eval(Scissors s); Outcome eval(Rock r); } class Paper implements Item { public Outcome compete(Item it) { return it.eval(this); } public Outcome eval(Paper p) { return DRAW; } public Outcome eval(Scissors s) { return WIN; } public Outcome eval(Rock r) { return LOSE; } public String toString() { return "Paper"; } } class Scissors implements Item { public Outcome compete(Item it) { return it.eval(this); } public Outcome eval(Paper p) { return LOSE; } public Outcome eval(Scissors s) { return DRAW; } public Outcome eval(Rock r) { return WIN; } public String toString() { return "Scissors"; } } class Rock implements Item { public Outcome compete(Item it) { return it.eval(this); } public Outcome eval(Paper p) { return WIN; } public Outcome eval(Scissors s) { return LOSE; } public Outcome eval(Rock r) { return DRAW; } public String toString() { return "Rock"; } } public class RoShamBo1 { static final int SIZE = 20; private static Random rand = new Random(47); public static Item newItem() { switch (rand.nextInt(3)) { default: case 0: return new Scissors(); case 1: return new Paper(); case 2: return new Rock(); } } public static void match(Item a, Item b) { System.out.println(a + " vs. " + b + ": " + a.compete(b)); } public static void main(String[] args) { for (int i = 0; i < SIZE; i++) match(newItem(), newItem()); } }
View Code - 使用enum實現多路分發
- enum的實例不能作為類型參數,不可以重載方法。
- 可以使用enum構造器初始化每個enum實例,並以一組結果作為參數如 ENUM_A( vsA_DRAW, vsB_LOSE, vsC_WIN ) 在比較方法中使用switch 判斷 返回 結果
package enums; import static enums.OutCome.*; public enum RoSham { PAPER(DRAW, LOSE, WIN), SCISSORS(WIN, DRAW, LOSE), ROCK(LOSE, WIN, DRAW); private OutCome vPAPER, vSCISSORS, vROCK; RoSham(OutCome paper, OutCome scissors, OutCome rock) { this.vPAPER = paper; this.vSCISSORS = scissors; this.vROCK = rock; } public OutCome complete(RoSham it) { switch (it) { default: case PAPER: return vPAPER; case SCISSORS: return vSCISSORS; case ROCK: return vROCK; } } public static void main(String[] args) { System.out.println(PAPER.complete(ROCK)); } }
View CodePAPER.complete()時把PAPER構造器中的結果與 OutCome 變量綁定,根據對比的參數返回對比結果,因此實例構造器中的參數位置非常重要、
- 使用EnumMap
- EnumMap實現真正的多路分發
package enums; import java.util.EnumMap; import static enums.OutCome.*; public enum RoShamBo { PAPER, SCISSORS, ROCK; static EnumMap<RoShamBo, EnumMap<RoShamBo, OutCome>> table = new EnumMap<RoShamBo, EnumMap<RoShamBo, OutCome>>(RoShamBo.class); static { for (RoShamBo it : RoShamBo.values()) { table.put(it, new EnumMap<RoShamBo, OutCome>(RoShamBo.class)); } initRow(PAPER, DRAW, LOSE, WIN); initRow(SCISSORS, WIN, DRAW, LOSE); initRow(ROCK, LOSE, WIN, DRAW); } static void initRow(RoShamBo it, OutCome vPAPER, OutCome vSCISSORS, OutCome vROCK) { EnumMap<RoShamBo, OutCome> row = RoShamBo.table.get(it); row.put(RoShamBo.PAPER, vPAPER); row.put(RoShamBo.SCISSORS, vSCISSORS); row.put(RoShamBo.ROCK, vROCK); } public OutCome complete(RoShamBo it) { return table.get(this).get(it); } public static void main(String[] args) { System.out.println(ROCK.complete(SCISSORS)); } }
View Codecomplete方法實現了2次分發
- EnumMap實現真正的多路分發
- 使用二維數組
- 簡單,速度快,代碼易懂,但是組數比較大時尺寸容易錯
private static OutCome[][] tables = { {DRAW, LOSE, WIN}, {WIN, DRAW, LOSE}, {LOSE, WIN, DRAW}, }; public OutCome completes (RoShamBo other) { return tables[this.ordinal()][other.ordinal()]; }
- 簡單,速度快,代碼易懂,但是組數比較大時尺寸容易錯
知識點
- 可以靜態導入枚舉類 直接使用枚舉實例 import static ......Color.* 最好使用靜態導入省去寫enum類。
《Java編程思想》筆記 第十九章 枚舉類型