第十九章 枚举类型


在这里插入图片描述
关键字enum可以将一组具名的值的有限集合创建为一种新的类型,而这些具名的值可以作为常规的程序组件使用

1.基本enum特性
  1. 创建enum时,编译器会为你生成一个相关的类,这个类继承自java.lang.Enum

  2. 方法
  • values():按enum中声明的顺序返回enum实例数组
  • ordinal():每隔enum实例在声明时的次序
  • getDeclaringClass():返回所属enum类
  • name():返回enum实例声明时的名字
  • ValueOf():根据给定的名字返回相应的enum实例,如果不存在给定名字的实例,将会抛出异常
  1. 将静态导入用于enum
  • import static enumerated.Spiciness.*;
2. 向enum中添加新方法
  1. 除了不能继承自一个enum之外,我们基本上可以将enum看作一个常规的类

  2. 如果你打算定义自己的方法,那么必须在enum实例序列的最后添加一个分号。同时Java要求你必须先定义enum实例(见Chapter19\Example01\OzWitch.java)

  3. 使用static import能够将enum实例的标识符代入当前的命名空间,所以无需在用enum来修饰enum实例。

  4. enum中的构造器与方法和普通的类没有区别,因为除了有少许限制外,enum就是一个普通的类

  5. 覆盖enum的方法
  • (1) 覆盖toString()方法,给我们提供了另一种方式来为枚举实例生成不同的字符串描述信息
  • (2) 覆盖enum的toString()方法与覆盖一般类方法没区别
3.switch语句中的enum
package Chapter19.Example02;//: enumerated/TrafficLight.java
// Enums in switch statements.
import static net.mindview.util.Print.*;

// Define an enum type:
enum Signal {
    
     GREEN, YELLOW, RED, }

public class TrafficLight {
    
    
  Signal color = Signal.RED;
  public void change() {
    
    
    switch(color) {
    
    
      // Note that you don't have to say Signal.RED
      // in the case statement:
      case RED:    color = Signal.GREEN;
                   break;
      case GREEN:  color = Signal.YELLOW;
                   break;
      case YELLOW: color = Signal.RED;
                   break;
    }
  }
  public String toString() {
    
    
    return "The traffic light is " + color;
  }
  public static void main(String[] args) {
    
    
    TrafficLight t = new TrafficLight();
    for(int i = 0; i < 7; i++) {
    
    
      print(t);
      t.change();
    }
  }
} /* Output:
The traffic light is RED
The traffic light is GREEN
The traffic light is YELLOW
The traffic light is RED
The traffic light is GREEN
The traffic light is YELLOW
The traffic light is RED
*///:~
  1. 在switch中使用enum,是enum提供的一项非常便利的功能
4.value()的神秘之处
  • value()是由编译器添加的static方法
5.实现,而非继承
  1. 所有的enum都继承自java.lang.Enum类。由于Java不支持多重继承,所以你的enum不能再继承其他类
  2. 在我们创建一个新的enum时,可以同时实现一个或多个接口
6.随机选取
  1. 我们可以利用泛型,从而使这个工作更一般化
  2. <T extends Enum>T是一个enum实例
package Chapter19.Example03;

import java.util.Random;

public class Enums {
    
    
    private static Random rand = new Random(47);

    public static <T extends Enum<T>> T random(Class<T> ec) {
    
    
        return random(ec.getEnumConstants());
    }

    public static <T> T random(T[] values) {
    
    
        return values[rand.nextInt(values.length)];
    }
}

7.使用接口组织枚举
  1. 在一个接口的内部,创建实现该接口的枚举,以此将元素进行分组,可以达到将枚举元素分类组织的目的
//: enumerated/menu/Meal2.java
package Chapter19.Example04;
import net.mindview.util.*;

public enum Meal2 {
    
    
  APPETIZER(Food.Appetizer.class),
  MAINCOURSE(Food.MainCourse.class),
  DESSERT(Food.Dessert.class),
  COFFEE(Food.Coffee.class);
  private Food[] values;
  private Meal2(Class<? extends Food> kind) {
    
    
    values = kind.getEnumConstants();
  }
  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 Food randomSelection() {
    
    
    return Enums.random(values);
  }
  public static void main(String[] args) {
    
    
    for(int i = 0; i < 5; i++) {
    
    
      for(Meal2 meal : Meal2.values()) {
    
    
        Food food = meal.randomSelection();
        System.out.println(food);
      }
      System.out.println("---");
    }
  }
} /* Same output as Meal.java *///:~
8.使用EnumSet替代标志
  1. EnumSet,是为了通过enum创建一种替代品,以替代传统的基于int的“位标志”。这些标志可以用来表示某种“开/关”信息
  2. 使用EnumSet的优点是:它在说明一个二进制位是否存在时,具有更好的表达能力,并且无需担心性能
  3. EnumSet的基础是long,当超过64个元素时,会增加一个long
9.使用EnumMap
  1. EnumMap是一种特殊的Map,它要求其中的键(key)必须来自一个enum
  2. EnumMap允许程序员改变值对象
10.常量相关的方法
  1. 要实现常量相关的方法,你需要为enum定义一个或多个abstract方法,然后为每个enum实例实现该抽象方法
  2. 我们不能真的将enum实例作为一个类型来使用
  3. 使用enum的职责链
  • 在职责链设计模式中,程序员以多种不同的方式来解决一个问题,然后将它们链接在一起。当一个请求到来时,它遍历这个链,直到链中的某个解决方案能够处理该请求
  1. 使用enum的状态机
  • 一个状态机可以具有有限个特定的状态,它通常根据输入,从一个状态转移到下一个状态,不过也可能存在瞬时状态(transient states),而一旦任务执行结束,状态机就会立刻离开瞬时状态
11.多路分发
  1. Java只支持单路分发:如果要执行的操作不止一个类型未知的对象时,那么Java的动态绑定机制只能处理其中一个的类型
  2. 要配置好多路分发需要很多的工序,不过要记住,它的好处在于方法调用时的优雅的语法,这避免了在一个方法中判定多个对象的类型的丑陋代码
//: enumerated/RoShamBo1.java
// Demonstration of multiple dispatching.
package Chapter19.Example05;
import java.util.*;

import static Chapter19.Example05.Outcome.*;

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());
  }
} /* Output:	
Rock vs. Rock: DRAW
Paper vs. Rock: WIN
Paper vs. Rock: WIN
Paper vs. Rock: WIN
Scissors vs. Paper: WIN
Scissors vs. Scissors: DRAW
Scissors vs. Paper: WIN
Rock vs. Paper: LOSE
Paper vs. Paper: DRAW
Rock vs. Paper: LOSE
Paper vs. Scissors: LOSE
Paper vs. Scissors: LOSE
Rock vs. Scissors: WIN
Rock vs. Paper: LOSE
Paper vs. Rock: WIN
Scissors vs. Paper: WIN
Paper vs. Scissors: LOSE
Paper vs. Scissors: LOSE
Paper vs. Scissors: LOSE
Paper vs. Scissors: LOSE
*///:~
package Chapter19.Example05;
public enum Outcome {
    
     WIN, LOSE, DRAW } ///:~
  1. 使用enum分发
  • enum实例不是类型,不能将enum实例作为参数的类型,所以无法重载eval()方法
  • 一种方式是使用构造器来初始化每个enum实例,并以“一组”结果作为参数。这二者放在一块,形成了类似查询表的结构
  1. 使用常量相关的方法
  • enum实例虽然可以具有不同的行为,但它们仍然不是类型,不能将其作为方法签名中的参数类型来使用。最好的办法是将enum用在switch语句中
  1. 使用EnumMap分发
  • 使用EnumMap能够实现“真正的”两路分发
  1. 使用二维数组
  • 由于使用的是数组,所以这种方式不太“安全”

猜你喜欢

转载自blog.csdn.net/Tianc666/article/details/108976804
今日推荐