1. 程式人生 > >《Java編程思想》筆記 第十九章 枚舉類型

《Java編程思想》筆記 第十九章 枚舉類型

void serial final eof coff rabl com 筆記 bstr

1.基本enum特征

  • 所有創建的枚舉類都繼承自抽象類 java.lang.Enum;
  1. 一個枚舉類,所有實例都要在第一句寫出以 ,隔開。 如果只有實例最後可以不加 ; 枚舉類因為繼承了Enum,所以再不能繼承別的類,任何類也不能繼承枚舉類(構造器默認為private)。
    public enum Color {
        RED,
        BLUE,
        YELLOW,
        PURPLE
    }
    註意 :RED,BLUE 這些是由 enum Color類調用默認private構造器創建的實例對象,和普通class相比只不過enum的實例對象只能在內部創建。時刻記著他們是一個實例對象。
  2. 枚舉類實例不能在外部 new 出來 ,因為枚舉類構造器為private 一般也不用聲明private,默認就是private,因為enum實例只能在編譯期間enum類內部被創建,但可以外部得到一個實例的引用。
            Color red  = Color.RED;
            Color blue = Color.BLUE;
  3. 枚舉類的一些方法
    1.  使用枚舉元素 Color.RED 或者 red
    2.  Color.values( ) 返回一個枚舉類數組,數組元素就是枚舉的實例。
    3.  red.ordinal() 方法返回該實例聲明的次序從0開始。
    4.  2個實例可用 == 比較
    5.  Enum 實現了 Comparable<E>和 Serializable 可以使用comparableTo( )方法,可以序列化
    6.  a. getDeclaringClass() 返回 Color.class 對象
    7.  a.name和a.toString( )方法一樣。
    8. valuesOf(string ) 返回一個實例對象 Color b = Color.valueOf("BLUE");
    9.  根據class對象返回實例 Color b = Color.valueOf( Color.class, "BLUE" )
  4. 通過帶參構造器為枚舉實例添加描述信息。調用 getDes()就可以的到當前對象的描述信息。
    public enum Color {
        RED("這是紅色"),
        BLUE("這是藍色"),
        YELLOW(
    "這是黃色"), PURPLE("這是紫色"); String des; Color( String s) { this.des = s; } public String getDes(){ return des; } }

    調用 getDes( )方法就可以的到當前對象的描述信息。

  5. 重寫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 方法
  1. enum 實例也可以用在 switch語句中
  2. values()方法不在Enum中 它是由編譯器添加的static方法。
  3. 編譯器還添加了一個valuesOf(String s)方法,這個只需要一個參數就可以的得到實例,而Enum的需要2個。
  4. 如果將enum向上轉型為Enum那麽values 和 valuesOf 無法再使用。
  5. 與values 方法有相同作用的就是Class對象的getEnumConstants(),如果class是枚舉類那麽返回元素數組,不是枚舉類返回null

3. 使用接口組織枚舉

  • 為了實現擴展enum 或者將enum分類,因為無法繼承所以靠擴展子類無法實現,可以利用接口來達到這些功能。
  1. 接口內部創建實現該接口的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;
        }
  2. 接口基礎上創建一個枚舉的枚舉,通過該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方法就可以的到該實例的所有元素。

  3. 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速度非常快,性能非常高。
  1. EnumSet 只能存放enum元素,不能插入空元素。
  2. 放入的元素位置和enum中保持一樣,它處於排序狀態,是一個有序Set.
  3. 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元素有限所以內部只是由數組實現。
  1. 這是一個有序map,保持enum的元素順序。
  2. EnumMap<Color,Object> map = new EnumMap<Color, Object>(Color.class);
  3. 方法和其他Map一樣。

6.實例對象添加方法

  • 為每一個enum實例(相當於常量)添加一個方法,讓他們有不同的行為。
  1. 為每一個實例添加不同行為的方法
    1. 在enum中創建一個或者多個abstract 方法,因為是enum的實例所以就得實現這些方法。
         RED{
              @Override
              String getInfo() {
                  return null;
              }
      
              @Override
              String getTime() {
                  return null;
              }
          };
          abstract String getInfo();
          abstract String getTime();
    2. enum也可以有main方法作為enum執行入口
    3. 常量添加方法後和有了類的行為,貌似和內部類一樣,但他們有著不同行為,enum的常量不能作為方法參數類型,因為他們不是類,只是enum類型的static final 實例
    4. 由於enum的常量是 static final 的所以常量的方法不能訪問外部類的非靜態方法。
  2. 覆蓋常量相關的方法
    1. enum中所有非抽象方法每個實例都可以調
    2. 如果不需要每個實例都實現抽象類,那麽就可以不用定義抽象類,每個實例各自實現方法,實現的方法可以覆蓋enum中的方法。
  3. 使用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
  4. enum狀態機
    • 狀態機可以具有 有限個 狀態,通常根據輸入,從一個狀態轉移到下一個狀態,也可以有瞬時狀態,一但任務結束就立刻離開瞬時狀態。

7.多路分發

  • 多種類型交互時有時並不能確定所有類型,如: NUM.complete(NUM) , NUM 是所有數字類型的超類,a.complete(b) ,a b可能是同種類型也可能不是同一種類型。
  • Java 動態綁定只能處理一種類型,屬於單路分發(分派),動態綁定能將complete綁定到分路a。只有方法調用才會執行動態綁定。
  1. 可以為每一個分發實現自己的動態綁定 技術分享圖片
    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
  2. 使用enum實現多路分發
    1. enum的實例不能作為類型參數,不可以重載方法。
    2. 可以使用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 Code

      PAPER.complete()時把PAPER構造器中的結果與 OutCome 變量綁定,根據對比的參數返回對比結果,因此實例構造器中的參數位置非常重要、

  3. 使用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 Code

      complete方法實現了2次分發

  4. 使用二維數組
    • 簡單,速度快,代碼易懂,但是組數比較大時尺寸容易錯
        private static OutCome[][] tables = {
              {DRAW, LOSE, WIN},
              {WIN, DRAW, LOSE},
              {LOSE, WIN, DRAW},
          };
      
          public OutCome completes (RoShamBo other) {
              return tables[this.ordinal()][other.ordinal()];
          }

知識點

  1. 可以靜態導入枚舉類 直接使用枚舉實例 import static ......Color.* 最好使用靜態導入省去寫enum類。

《Java編程思想》筆記 第十九章 枚舉類型