• 《Java编程思想》笔记 第十九章 枚举类型


    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类。
  • 相关阅读:
    月食照片
    宾得镜头大全与发展史
    月食照片
    关于镜头系数的疑问
    经验和教训
    常用正则表达式
    12月19日
    部長面談
    周六
    异度空间
  • 原文地址:https://www.cnblogs.com/mibloom/p/9508077.html
Copyright © 2020-2023  润新知