《Java 编程思想》--第十九章:枚举类型

来源:互联网 发布:boljoo蒙语软件下载 编辑:程序博客网 时间:2024/05/16 10:11
  1. 关键字enum可以将一组具名的值得优先级和创建为一种新的类型,而这些具名的值可以作为常规的程序组件使用
  2. enum的定义和使用方式方式:
    1. 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      enumShrubbery {GROUND, CRAWLING, HANGING}
       
      publicclass EnumClass {
       
          publicstatic void main(String[] args) {
              // TODO Auto-generated method stub
              for(Shrubbery s : Shrubbery.values()){
                  System.out.println(s + " ordinal:" + s.ordinal());
                  System.out.println(s.compareTo(Shrubbery.CRAWLING) + "  ");
                  System.out.println(s.equals(Shrubbery.CRAWLING));
                  System.out.println(s == Shrubbery.CRAWLING);
                  System.out.println(s.getDeclaringClass());
                  System.out.println(s.name());
                  System.out.println();
              }
          }
       
      }

    2. values()返回enum实例数组,并且该数组的元素严格保持其在enum中声明时的顺序
    3. ordinal()方法返回一个int值,表示每个enum实例在声明时的次序
    4. 可以直接使用==来比较enum实例,编译器会自动为你提供equals()和hashCode()方法。Enum类实现了Comparable接口,所以它具有compareTo()方法。同时它还实现了Serializable接口
    5. getDeclaringClass()方法可以获得其所属的enum类
    6. name()方法与使用toString()方法效果相同,返回enum实例声明时的名字
    7. valueOf()是Enum中定义的static方法,根据给定的名字返回相应的enum实例,如果不存在给定名字的实例,将会抛出异常
  3. 静态导入enum:
    1. 使用方式:
      1. 1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        packageenumTest;
         
        publicenum Spiciness {
            NOT, MILD, MEDIUM, HOT, FLAMING
        }
         
        packageenumTest;
         
        importstatic enumTest.Spiciness.*;
         
        publicclass Burrito {
         
            publicstatic void main(String[] args) {
                System.out.println(NOT.ordinal());
                System.out.println(MILD);
                System.out.println(MEDIUM.equals(MILD));
            }
         
        }

      2. 通过static import,可以使enum实例的标识符带入当前的命名空间,所以无需再用enum类型来修是谁enum实例
  4. enum不能被继承,除此之外可以将其看作是一个常规的类,可以向其中添加方法,甚至可以有main()方法
    1. 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      packageenumTest;
       
      publicenum OzWitch {
          WEST("This is west"),
          NORTH("This is north"),
          EAST("This is east"),
          SOUTH("This is center");
           
          privateString description;
          privateOzWitch(String description) {
              this.description = description;
          }
           
          publicString getDescription(){
              returndescription;
          }
           
          publicstatic void main (String[] args) {
              for(OzWitch witch : OzWitch.values()){
                  System.out.println(witch.toString() + ":"+ witch.getDescription());
              }
          }
      }

  5. values()的神秘之处
    1. 编译器创建的enum类都继承自Enum类,然而Enum类中并没有values()方法。values()方法是由编译器添加的static方法,在创建一个enum类时,编译器还为其添加了valueOf()方法,在Enum中的valueOf()需要两个参数,而这个新增的valueOf()方法只需要一个
    2. 编译器还将enum类标记为final类,所以无法继承自enum类
    3. 由于values()方法是由编译器插入到enum定义中的static方法,所以如果将enum实例向上转型为Enum,那么values()方法就不可访问了。
    4. 在Class中有一个getEnumConstants()方法, 可以通过Class对象取得所有的enum实例。这个方法时Class上的方法,甚至可以对不是枚举的类调用此方法并返回null
  6. 实现,而非继承
    1. 所有的enum类都继承自java.lang.Enum类,由于Java不支持多重继承,所以enum不能继承其他类
  7. 随机选取:
    1. 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      packageenumTest;
       
      enumActivity {SITTING, LYING, STANDING, HOPPING, RUNNING, DODGING, JUMPING, FALLING, FLYING}
       
      publicclass RandomTest {
       
          publicstatic void main(String[] args) {
              for(inti = 0; i < 20; i++){
                  System.out.println(Enums.random(Activity.class));
              }
          }
       
      }
       
      packageenumTest;
       
      importjava.util.Random;
       
      publicclass Enums {
       
          privatestatic Random rand = newRandom(47);
           
          publicstatic <T extendsEnum<T>>T random(Class<T> ec){
              returnrandom(ec.getEnumConstants());
          }
           
          publicstatic <T> T random(T[] values){
              returnvalues[rand.nextInt(values.length)];
          }
       
      }

  8. 使用接口组织枚举:
    1. 对于enum而言,实现接口是使其子类化的唯一方法,所以嵌入在Food中的每个enum都实现了Food接口
      1. 1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        packageenumTest;
         
        publicinterface Food {
            enumAppetizer implementsFood{
                SALAD, SOUP, SPRINT_ROLLS;
            }
             
            enumMainCourse implementsFood{
                LASAGNE, BURRITO, PAD_THAI, LENTITLS, HUMMOUS, VINDALOO;
            }
            //And so on....
        }
         
        packageenumTest;
         
        importstatic enumTest.Food.*;
         
        publicclass TypeOfFood {
         
            publicstatic void main(String[] args) {
                Food food = Appetizer.SALAD;
                food = MainCourse.BURRITO;
            }
         
        }

    2. 如果类型数量较多,接口就不如enum好用了,可以建立枚举的枚举,甚至是在enum中嵌套enum
      1. 1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        packageenumTest;
         
        publicenum Meal {
            APPETIZER(Food.Appetizer.class),
            MAINCOURSE(Food.MainCourse.class),
            DESSERT(Food.Dessert.class),
            COFFEE(Food.Coffee.class);
            privateFood[] values;
             
            privateMeal(Class<? extendsFood> kind){
                values = kind.getEnumConstants();
            }
             
            publicinterface Food {
                enumAppetizer implementsFood{
                    SALAD, SOUP, SPRING_ROLLS;
                }
                enumMainCourse implementsFood{
                    LASAGNE, BURRITO, PAD_THAI, LENTILS, HUMMOUS, VINDALLO;
                }
                enumDessert implementsFood{
                    TIRAMISU, GELATO, BLACK_FOREST_CAKE, FRUIT, VREME_CARAMEL;
                }
                enumCoffee implementsFood {
                    BLACK_COFFEE, DECAF_COFFEE, ESPRESSO, LATTE, CAPPUCCINO, TEA, HERB_TEA;
                }
            }
             
            publicFood randomSelection(){
                returnEnums.random(values);
            }
             
            publicstatic void main (String[] args){
                for(inti = 0; i < 5; i++){
                    for(Meal meal : Meal.values()){
                        Food food = meal.randomSelection();
                        System.out.println(food);
                    }
                    System.out.println("---");
                }
            }
        }

  9. 使用EnumSet替代标志:
    1. Java SE5中引入EnumSet,为了通过enum创建一种替代品,以替代传统的基于int的“位操作”。这种标志可以用来表示某种“开/关”信息。
    2. EnumSet的设计充分考虑到了速度因素,其操作与HashSet相比,非常的快,就其内部而言,它(可能)就是将一个long值作为b比特向量。
    3. 使用EnumSet的优点是:说明一个二进制位是否存在,具有更好的表达能力,并且无需担心性能
    4. EnumSet的元素必须来源于一个enum
    5. 使用实例:
      1. 1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        packageenumTest;
         
        importstatic enumTest.AlarmPoints.*;
         
        importjava.util.EnumSet;
         
        publicclass EnumSets {
         
            publicstatic void main(String[] args) {
                EnumSet<AlarmPoints> points = EnumSet.noneOf(AlarmPoints.class);
                points.add(BATHROOM);
                System.out.println(points);
                points.addAll(EnumSet.of(STAIR1, STAIR2, KITCHEN));
                System.out.println(points);
                points = EnumSet.allOf(AlarmPoints.class);
                System.out.println(points);
                points.removeAll(EnumSet.of(STAIR1, STAIR2, KITCHEN));
                System.out.println(points);
                points.removeAll(EnumSet.range(OFFICE1,OFFICE4));
                System.out.println(points);
                points = EnumSet.complementOf(points);
                System.out.println(points);
            }
         
        }

      2. 1
        2
        3
        4
        5
        packageenumTest;
         
        publicenum AlarmPoints {
            STAIR1, STAIR2, LOBBY, OFFICE1, OFFICE2, OFFICE3, OFFICE4, BATHROOM, UTILITY, KITCHEN
        }
      3. of方法被重载了很多次,因为其实只是用可变参数已经可以解决整个问题,但是对比显示的参数会有一点性能损失。当使用一个参数或多过5个参数时,调用的僵尸使用可变参数的of()方法,由于使用一个参数时,编译器不会构造可变参数的数组所以调用一个参数没有额外的性能消耗
    6. EnumSet的基础是long,一个long值有64位,但EnumSet可以应用于多过64个元素的enum,所以猜测,Enum会在必要的时候增加一个long
  10. 使用EnumMap:
    1. EnumMap是一种特殊的Map,它要求其中的键必须来自一个enum。由于enum本身的限制,所以EnumMap在内部可由数组实现,因此EnumMap的速度非常快。除了必须使用enum的实例作为键来调用put()方法以外,其他和一般的Map差不多
    2. 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      packageenumTest;
       
      importstatic enumTest.AlarmPoints.*;
       
      importjava.util.EnumMap;
      importjava.util.Map;
       
      publicclass EnumMaps {
       
          interfaceCommand{
              voidaction();
          }
           
          publicstatic void main(String[] args) {
              EnumMap<AlarmPoints, Command> em = newEnumMap<AlarmPoints, Command>(AlarmPoints.class);
              em.put(KITCHEN,newCommand() {
       
                  @Override
                  publicvoid action() {
                      System.out.println("Kitchen fire");
                  }
                   
              });
              em.put(BATHROOM,newCommand() {
       
                  @Override
                  publicvoid action() {
                      System.out.println("Bathroom fire");
                  }
                   
              });
               
              for(Map.Entry<AlarmPoints, Command> e : em.entrySet()){
                  System.out.print(e.getKey() + ":");
                  e.getValue().action();
              }
          }
      }
       
      packageenumTest;
       
      publicenum AlarmPoints {
          STAIR1, STAIR2, LOBBY, OFFICE1, OFFICE2, OFFICE3, OFFICE4, BATHROOM, UTILITY, KITCHEN
      }
    3. 与EnumSet一样,EnumMap中的顺序由enum实例决定
  11. 可以使用enum实现职责连
  12. 枚举类型非常适合用来创建状态机。一个状态机可以具有有限个特定的状态,它通常根据输入,从一个状态转移到下一个状态,不过也可能存在瞬时状态,而一旦人数执行结束,状态机就会立即离开瞬时状态
原创粉丝点击