thinking-in-java(19)枚举类型

来源:互联网 发布:java web mail 编辑:程序博客网 时间:2024/05/21 10:00
【0】开场白
1)关键字 enum
可以将一组具名的值的有限集合创建为一种新的类型,而这些具名的值可以作为常规的程序组件使用;
2)所有的枚举类都继承自 Enum,通过 enumClass.getSuperclass() = class java.lang.Enum 得知。  Enum的源码如下(本文斗胆把 Enum 称为 枚举基类,enum称为枚举类,enum中声明的成员称为枚举实例序列)
// java.lang.Enum枚举类源码public abstract class Enum<E extends Enum<E>>        implements Comparable<E>, Serializable {    private final String name;    public final String name() {        return name;    }    private final int ordinal;    public final int ordinal() {        return ordinal;    }    protected Enum(String name, int ordinal) {        this.name = name;        this.ordinal = ordinal;    }    public String toString() {        return name;    }    public final boolean equals(Object other) {        return this==other;    }    public final int hashCode() {        return super.hashCode();    }    protected final Object clone() throws CloneNotSupportedException {        throw new CloneNotSupportedException();    }    public final int compareTo(E o) {        Enum<?> other = (Enum<?>)o;        Enum<E> self = this;        if (self.getClass() != other.getClass() && // optimization            self.getDeclaringClass() != other.getDeclaringClass())            throw new ClassCastException();        return self.ordinal - other.ordinal;    }    @SuppressWarnings("unchecked")    public final Class<E> getDeclaringClass() {        Class<?> clazz = getClass();        Class<?> zuper = clazz.getSuperclass();        return (zuper == Enum.class) ? (Class<E>)clazz : (Class<E>)zuper;    }    public static <T extends Enum<T>> T valueOf(Class<T> enumType,                                                String name) {        T result = enumType.enumConstantDirectory().get(name);        if (result != null)            return result;        if (name == null)            throw new NullPointerException("Name is null");        throw new IllegalArgumentException(            "No enum constant " + enumType.getCanonicalName() + "." + name);    }    /**     * enum classes cannot have finalize methods.     */    protected final void finalize() { }    /**     * prevent default deserialization     */    private void readObject(ObjectInputStream in) throws IOException,        ClassNotFoundException {        throw new InvalidObjectException("can't deserialize enum");    }    private void readObjectNoData() throws ObjectStreamException {        throw new InvalidObjectException("can't deserialize enum");    }}
【19.1】基本enum特性
【荔枝-基本enum特性】

/* 荔枝-基本enum特性 */enum Shrubbery {GROUND, CRAWLING, HANGING}public class EnumClass {public static void main(String[] args) {// Shrubbery.values() 返回 enum 实例的数组(数组中的元素严格保持在enum中声明的顺序)for (Shrubbery s : Shrubbery.values()) {print("s.ordinal() = " + s.ordinal()); //  s.ordinal() 返回enum实例在声明时的次序;printnb("s.compareTo(Shrubbery.CRAWLING) = " + s.compareTo(Shrubbery.CRAWLING));printnb("\ns.equals(Shrubbery.CRAWLING) = " + s.equals(Shrubbery.CRAWLING));print("\n(s == Shrubbery.CRAWLING) = " + (s == Shrubbery.CRAWLING));print("s.getDeclaringClass() = " + s.getDeclaringClass());print("s.name() = " + s.name());print("----------------------");}// Produce an enum value from a string name:for (String s : "HANGING CRAWLING GROUND".split(" ")) {Shrubbery shrub = Enum.valueOf(Shrubbery.class, s);print("Enum.valueOf(Shrubbery.class, s) = " + shrub);}}}/*s.ordinal() = 0s.compareTo(Shrubbery.CRAWLING) = -1s.equals(Shrubbery.CRAWLING) = false(s == Shrubbery.CRAWLING) = falses.getDeclaringClass() = class chapter19.Shrubberys.name() = GROUND----------------------s.ordinal() = 1s.compareTo(Shrubbery.CRAWLING) = 0s.equals(Shrubbery.CRAWLING) = true(s == Shrubbery.CRAWLING) = trues.getDeclaringClass() = class chapter19.Shrubberys.name() = CRAWLING----------------------s.ordinal() = 2s.compareTo(Shrubbery.CRAWLING) = 1s.equals(Shrubbery.CRAWLING) = false(s == Shrubbery.CRAWLING) = falses.getDeclaringClass() = class chapter19.Shrubberys.name() = HANGING----------------------Enum.valueOf(Shrubbery.class, s) = HANGINGEnum.valueOf(Shrubbery.class, s) = CRAWLINGEnum.valueOf(Shrubbery.class, s) = GROUND*/
【代码解说】
解说1)
可以使用 == 来比较 enum实例,编译器自动提供了 equals() 和 hashCode() 方法;
解说2)Enum类实现了 Comparable接口,所以具有 compareTO()方法,Enum 还实现了 Serializable 接口;
解说3)enum实例的getDeclaringClass() 方法返回 enum实例所属的enum类;
解说4)enum实例的 name()方法返回 实例声明时的名字;
解说5)valueOf()方法是Enum类的静态方法,返回相应的 enum 实例数组;(数组元素按照声明时的顺序排序)


【19.1.1】将静态导入用于 enum
1)使用 static import : 
将 enum实例的标识符带入当前的命名空间,无需再用enum类型修饰 enum 实例;
【荔枝-使用静态导入引入 枚举类】
package chapter5;import static chapter5.Spiciness5.*; //  使用静态导入public class Burrito {Spiciness5 degree;public Burrito(Spiciness5 degree) {this.degree = degree;}public void describe() {System.out.print("This burrito is ");switch (degree) {case NOT:System.out.println("not spicy at all.");break;case MILD:case MEDIUM:System.out.println("a little hot.");break;case HOT:case FLAMING:default:System.out.println("maybe too hot.");}}public static void main(String[] args) {//Burrito plain = new Burrito(Spiciness5.NOT);//Burrito greenChile = new Burrito(Spiciness5.MEDIUM);//Burrito jalapeno = new Burrito(Spiciness5.HOT);// 不使用 enum 类修饰的语法Burrito plain = new Burrito(NOT);Burrito greenChile = new Burrito(MEDIUM);Burrito jalapeno = new Burrito(HOT);plain.describe();greenChile.describe();jalapeno.describe();}}/*This burrito is not spicy at all.This burrito is a little hot.This burrito is maybe too hot.*/
【荔枝【编译报错】-使用静态导入的前提条件】
package chapter19;import chapter5.Spiciness5; // 但是可以使用普通导入(无法使用静态导入)// 荔枝【编译报错】-使用静态导入的前提条件(Burrito 与  Spiciness5 在同一个包,)// 这里的荔枝是 Spiciness5 在chapter5 包,而Burrito在chapter19包,所以编译报错;//import static chapter5.Spiciness5; public class Burrito {Spiciness5 degree;public Burrito(Spiciness5 degree) {this.degree = degree;}public String toString() {return "Burrito is " + degree;}public static void main(String[] args) {System.out.println(new Burrito(Spiciness5.NOT));System.out.println(new Burrito(Spiciness5.MEDIUM));System.out.println(new Burrito(Spiciness5.HOT));}}
【19.2】向enum中添加新方法
1)enum没有继承机制,除此之外,enum类是一个常规类,有自己的方法和main方法;


【荔枝-每个枚举实例能够返回自身的描述】

// 荔枝-每个枚举实例能够返回自身的描述public enum OzWitch {// Instances must be defined first, before methods:WEST("Miss Gulch, aka the Wicked Witch of the West"), NORTH("Glinda, the Good Witch of the North"), EAST("Wicked Witch of the East, wearer of the Ruby Slippers, crushed by Dorothy's house"), SOUTH("Good by inference, but missing");private String description;// Constructor must be package or private access:// 构造函数必须是包或私有访问private OzWitch(String description) {this.description = description;}// enum类有自己的方法public String getDescription() {return description;}// enum类有 main() 方法public static void main(String[] args) {for (OzWitch witch : OzWitch.values()) // OzWitch.values() 返回 enum 实例数组print(witch + ": " + witch.getDescription());}} /*WEST: Miss Gulch, aka the Wicked Witch of the WestNORTH: Glinda, the Good Witch of the NorthEAST: Wicked Witch of the East, wearer of the Ruby Slippers, crushed by Dorothy's houseSOUTH: Good by inference, but missing*/
【代码解说】
解说1)
在enum 实例序列的最后添加一个分号: 如 SOUTH("Good by inference, but missing");
解说2)java要求先定义 enum 实例,后定义 方法 和 属性;否则编译报错;
解说3)荔枝中,enum类的构造方法为private,但不会影响创建 enum实例(不影响包之外的类调用该枚举实例),因为只能在 enum 内部使用构造器创建 enum 实例;

【19.2.1】覆盖 enum 的方法
1)通过覆盖 enum 类的 toString() 方法来实现 描述enum实例名字;
【荔枝-覆盖 enum类的toString() 方法】

// 荔枝-覆盖 enum类的toString() 方法public enum SpaceShip {// 在enum 实例序列的最后添加一个分号:SCOUT, CARGO, TRANSPORT, CRUISER, BATTLESHIP, MOTHERSHIP; // 覆盖toString() 方法@Overridepublic String toString() {String id = name();String lower = id.substring(1).toLowerCase();return id.charAt(0) + lower;}public static void main(String[] args) {for (SpaceShip s : values()) { // values 返回 枚举实例数组System.out.println(s);}}}/*ScoutCargoTransportCruiserBattleshipMothership*/
【19.3】switch语句中的enum:这是enum提供的一项非常便利的功能
1)enum类的ordinal()方法:
获取该 enum实例的声明次序;
2)case语句中不必使用 enum类型修饰 enum实例;


【荔枝-case语句中不必使用 enum类型修饰 enum实例】

// 定义一个枚举类enum Signal {GREEN, YELLOW, RED,}// 荔枝(使用 enum 构建一个小型状态机)-case语句中不必使用 enum类型修饰 enum实例// 不必使用 enum类型修饰 enum实例,还可以通过 静态导入来引入同一个包下的枚举类实现public class TrafficLight {Signal color = Signal.RED;public void change() {switch (color) {  // switch 块 与 enum 实例的结合使用case RED: // 不必使用 enum类型修饰 enum实例,不必用 Signal.REDcolor = Signal.GREEN;break;case GREEN: // 不必使用 enum类型修饰 enum实例,同理color = Signal.YELLOW;break;case YELLOW: // 不必使用 enum类型修饰 enum实例,同理color = Signal.RED;break;//注意:这里没有default 语句}}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);t.change();}}}/*The traffic light is REDThe traffic light is GREENThe traffic light is YELLOWThe traffic light is REDThe traffic light is GREENThe traffic light is YELLOWThe traffic light is RED*/
【代码解说】
解说1)
当 switch 与 enum 实例结合使用的时候,编译器允许没有 default 语句;
解说2)注释掉其中一个case语句,编译器也不会报错;所以必须确保 switch中的case 覆盖了所有 enum 枚举实例分支;
解说3)case语句中有return 语句:编译器就会抱怨没有 default 语句了;

【19.4】values()的神秘之处
1)Enum类并没有values()方法:
编译器创建的 enum类对象 都继承自 Enum类;但是Enum类并没有 values() 方法;


【荔枝-Enum类没有定义 values()方法,但Enum实例有 values()方法,这个values()方法是编译器加上去的】

// 定义一个枚举类enum Explore {HERE, THERE}// 荔枝-Enum类没有定义 values()方法,但Enum实例有 values()方法,这个values()方法是编译器加上去的。public class Reflection {public static Set<String> analyze(Class<?> enumClass) {print("\n----- enumClass = " + enumClass + " -----");print("Interfaces:");// 通过反射获取 enumClass对应的类实现的接口列表for (Type t : enumClass.getGenericInterfaces()) // java.lang.reflect.Typeprint("t = " + t);print("enumClass.getSuperclass() = " + enumClass.getSuperclass());Set<String> methods = new TreeSet<String>();// 通过反射获取 enumClass对应的类的方法列表for (Method m : enumClass.getMethods())methods.add(m.getName());print("methods = " + methods);return methods;}public static void main(String[] args) {Set<String> exploreMethods = analyze(Explore.class); // Explore 有 values()方法 Set<String> enumMethods = analyze(Enum.class); // Enum 没有 values()方法print("enumMethods = " + enumMethods + "\n");print("exploreMethods.containsAll(enumMethods) = "+ exploreMethods.containsAll(enumMethods));exploreMethods.removeAll(enumMethods);print("exploreMethods.removeAll(enumMethods); exploreMethods = " + exploreMethods);// Decompile the code for the enum:OSExecute.command("javap chapter19.Explore"); // 对 chapter19.Explore 进行反编译}}/*----- enumClass = class chapter19.Explore -----Interfaces:// 父类enumClass.getSuperclass() = class java.lang.Enum// enum 实例的方法列表 methods = [compareTo, equals, getClass, getDeclaringClass, hashCode, name, notify, notifyAll, ordinal, toString, valueOf, values, wait]----- enumClass = class java.lang.Enum -----Interfaces:t = java.lang.Comparable<E>t = interface java.io.SerializableenumClass.getSuperclass() = class java.lang.Object// Enum 类的方法列表methods = [compareTo, equals, getClass, getDeclaringClass, hashCode, name, notify, notifyAll, ordinal, toString, valueOf, wait]enumMethods = [compareTo, equals, getClass, getDeclaringClass, hashCode, name, notify, notifyAll, ordinal, toString, valueOf, wait]exploreMethods.containsAll(enumMethods) = trueexploreMethods.removeAll(enumMethods); exploreMethods = [values]Compiled from "Reflection.java"  // 对 chapter19.Explore 进行反编译final class chapter19.Explore extends java.lang.Enum<chapter19.Explore> {   public static final chapter19.Explore HERE;  public static final chapter19.Explore THERE;  public static chapter19.Explore[] values();  public static chapter19.Explore valueOf(java.lang.String);  static {};}*/
【代码解说】
解说1)
enum实例的values() 方法:是编译器添加的 static方法;Enum 类中没有定义 values() 方法;
解说2)enum实例的 valueOf(java.lang.String) 方法(一个参数):也是编译器添加的;覆盖掉了 Enum.valueOf() 方法了;
解说3)Enum类定义了一个 valueOf() 方法(有两个参数);
// Enum.valueOf() 源码(两个参数)public static <T extends Enum<T>> T valueOf(Class<T> enumType,                                                String name) {        T result = enumType.enumConstantDirectory().get(name);        if (result != null)            return result;        if (name == null)            throw new NullPointerException("Name is null");        throw new IllegalArgumentException(            "No enum constant " + enumType.getCanonicalName() + "." + name);    }
解说4)enum 实例被 final修饰:所以enum 无法继承;


2)如果将 enum 实例向上转型为 Enum 类型,那么values()方法就不可访问了;

3)Class.getEnumConstants()方法: 通过该对象取得所有 enum 实例;


【荔枝-通过 Class.getEnumConstants()方法取得所有 enum 实例】

enum Search {HITHER, YON}// 荔枝-通过 Class.getEnumConstants()方法取得所有 enum 实例public class UpcastEnum {public static void main(String[] args) {Search[] vals = Search.values();Enum e = Search.HITHER; // Upcast-向上转型// e.values(); // Enum 类没有 values() 方法,编译报错:// 通过 Class.getEnumConstants()方法 获取所有 enum 实例数组for (Enum en : e.getClass().getEnumConstants())System.out.println(en);}}/*HITHERYON*/
4)不是枚举的类也可以调用 Class.getEnumConstants() 方法:


【荔枝-不是枚举的类调用 Class.getEnumConstants() 方法(报空指针异常)】

// 荔枝-不是枚举的类调用 Class.getEnumConstants() 方法public class NonEnum {public static void main(String[] args) {Class<Integer> intClass = Integer.class;try {// Integer.class.getEnumConstants() 报空指针异常for (Object en : intClass.getEnumConstants())System.out.println(en);} catch (Exception e) {System.out.println(e);}}} /* * Output: java.lang.NullPointerException */// :~ 
【19.5】实现,而非继承
1)所有 enum 类都继承自 java.lang.Enum 类;

2)由于不能多继承,所以只能实现;

【荔枝-所有 enum 类都继承自 java.lang.Enum 类,只能实现】

// 荔枝-所有 enum 类都继承自 java.lang.Enum 类,只能实现enum CartoonCharacter implements Generator<CartoonCharacter> {SLAPPY, SPANKY, PUNCHY, SILLY, BOUNCY, NUTTY, BOB;private Random rand = new Random(47);public CartoonCharacter next() {return values()[rand.nextInt(values().length)];}}public class EnumImplementation {public static <T> void printNext(Generator<T> rg) { // 这里传入 Generator,是策略模式System.out.print(rg.next() + ", ");}public static void main(String[] args) {// 选择任何一个实例CartoonCharacter cc = CartoonCharacter.BOB;for (int i = 0; i < 10; i++)printNext(cc);}} /*BOB, PUNCHY, BOB, SPANKY, NUTTY, PUNCHY, SLAPPY, NUTTY, NUTTY, SLAPPY, */
【19.6】随机选取
1)从enum实例中随机选择enum实例;


【荔枝-随机选取 enum 实例】

// 荔枝-随机选取 enum 实例public class Enums {private static Random rand = new Random(47);// 通过反射 Class.getEnumConstants() 来随机选取public static <T extends Enum<T>> T random(Class<T> ec) {// 通过 Class.getEnumConstants()方法取得所有 enum 实例return random(ec.getEnumConstants());}public static <T> T random(T[] values) {return values[rand.nextInt(values.length)]; // 随机选取}} // /:~
其中,<T extends Enum<T>> 表示 T 是一个 enum 实例;将 Class<T> 作为参数的话,就可以利用 Class对象得到 enum 实例的数组了;

【测试荔枝-随机选取 enum 实例】
// 测试荔枝-随机选取 enum 实例enum Activity {SITTING, LYING, STANDING, HOPPING, RUNNING, DODGING, JUMPING, FALLING, FLYING}public class RandomTest {public static void main(String[] args) {for (int i = 0; i < 20; i++)// 这里必须传入一个 enum是的 class 引用System.out.print(Enums.random(Activity.class) + " ");}} /*STANDING FLYING RUNNING STANDING RUNNING STANDING LYING DODGING SITTING RUNNING HOPPING HOPPING HOPPING RUNNING STANDING LYING FALLING RUNNING FLYING LYING */
【19.7】使用接口组织枚举
1)扩展枚举的需求:
有时候希望扩展原 enum中的元素,有时回使用子类将一个 enum 中的元素进行分组;
2)在接口内部,创建实现该接口的枚举类,以此来扩展原枚举类:以此将元素进行分组,达到将枚举元素分类组织的目的;


【荔枝-在接口内部,创建实现该接口的枚举类,以此来扩展原枚举类】

// 荔枝-在接口内部,创建实现该接口的枚举类,以此来扩展原枚举类// 将 Food 进行分组;且所有的枚举类都是 Food 类型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;}} // /:~

【代码解说】
解说1)实现接口: 
是扩展枚举类的唯一办法;
解说2)上述实现Food接口的所有枚举类:  都是Food类型,属于Food类下的多个分组;

【测试荔枝-在接口内部,创建实现该接口的枚举类,以此来扩展原枚举类】
//测试荔枝-在接口内部,创建实现该接口的枚举类,以此来扩展原枚举类public class TypeOfFood {public static void main(String[] args) {Food food = Appetizer.SALAD;food = MainCourse.LASAGNE;food = Dessert.GELATO;food = Coffee.CAPPUCCINO;}} // /:~
3)当需要与一大堆类型交互时,接口没有enum枚举类好用


【荔枝-如何创建一个枚举的枚举类】

// 荔枝-创建一个枚举的枚举public enum Course {APPETIZER(Food.Appetizer.class),  // 这里调用的是 Course 的私有构造器MAINCOURSE(Food.MainCourse.class), DESSERT(Food.Dessert.class), COFFEE(Food.Coffee.class);private Food[] values;// 通过 Class.getEnumConstants()方法取得所有 enum 实例private Course(Class<? extends Food> kind) { // 私有构造器values = kind.getEnumConstants();}public Food randomSelection() {return Enums.random(values);}} // /:~
【测试荔枝-创建一个枚举的枚举】
// 测试荔枝-创建一个枚举的枚举public class Meal {public static void main(String[] args) {for (int i = 0; i < 5; i++) {for (Course course : Course.values()) {Food food = course.randomSelection();System.out.print(food + ", ");}System.out.println("\n----------------------------------------");}}} /* 获得枚举的枚举值SPRING_ROLLS, VINDALOO, FRUIT, DECAF_COFFEE, ----------------------------------------SOUP, VINDALOO, FRUIT, TEA, ----------------------------------------SALAD, BURRITO, FRUIT, TEA, ----------------------------------------SALAD, BURRITO, CREME_CARAMEL, LATTE, ----------------------------------------SOUP, BURRITO, TIRAMISU, ESPRESSO, ----------------------------------------*/
4)将一个枚举嵌套在另外一个枚举内部;


【荔枝-将一个枚举嵌套在另外一个枚举内部】

// 荔枝-将一个枚举嵌套在另外一个枚举内部;enum SecurityCategory {STOCK(Security.Stock.class), BOND(Security.Bond.class); // 两个enum 实例Security[] values;SecurityCategory(Class<? extends Security> kind) {values = kind.getEnumConstants();}// 这个接口看做是枚举类的集合。被嵌套在 枚举类 SecurityCategory 的内部interface Security {// 通过实现接口来扩展枚举实例个数enum Stock implements Security {  // 内部枚举类SHORT, LONG, MARGIN}enum Bond implements Security {MUNICIPAL, JUNK}}public Security randomSelection() { // 随机选择枚举return Enums.random(values);}public static void main(String[] args) {for (int i = 0; i < 10; i++) {SecurityCategory category = Enums.random(SecurityCategory.class); // 随机选择一个 SecurityCategory 枚举实例System.out.println(category + ": " + category.randomSelection()); // 打印随机选择的 枚举实例 }}} /*BOND: MUNICIPALBOND: MUNICIPALSTOCK: MARGINSTOCK: MARGINBOND: JUNKSTOCK: SHORTSTOCK: LONGSTOCK: LONGBOND: MUNICIPALBOND: JUNK*/
【代码解说】
解说1)Security接口的作用: 
将其所包含的 enum 实例组成成公共类型;

【第二个荔枝-将一个枚举嵌套在另外一个枚举内部】 ==== 1712200055
// 第二个荔枝-将一个枚举嵌套在另外一个枚举内部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.print(food + ", ");}System.out.println("\n---------------------------------------------");}}} /*SPRING_ROLLS, VINDALOO, FRUIT, DECAF_COFFEE, ---------------------------------------------SOUP, VINDALOO, FRUIT, TEA, ---------------------------------------------SALAD, BURRITO, FRUIT, TEA, ---------------------------------------------SALAD, BURRITO, CREME_CARAMEL, LATTE, ---------------------------------------------SOUP, BURRITO, TIRAMISU, ESPRESSO, ---------------------------------------------*/
【19.8】 使用 EnumSet 替代标志
1)枚举类:
要求所有枚举实例 都是唯一的,而Set保持不重复对象,所以enum类和 Set 很相像;但 enum中无法删除和添加 枚举实例;
2)EnumSet:java se5 引入的替代品,替代传统 的基于 int 的位标志;其优点是,判断一个二进制位是否存在时,具有更好的表达能力,无需担心性能;
3)EnumSet是一种容器:其元素必须来自同一个 枚举类的实例序列;
// java.util.EnumSet 源码public abstract class EnumSet<E extends Enum<E>> extends AbstractSet<E>    implements Cloneable, java.io.Serializable
【荔枝-EnumSet 操作枚举实例】
// 荔枝-EnumSet 操作枚举实例package chapter19;//: enumerated/EnumSets.java//Operations on EnumSetsimport java.util.*;import static net.mindview.util.Print.*;// 这里还采用了静态导入,这样使得枚举实例无需枚举基类Enum修饰;前提是 枚举类AlarmPoints 与 使用枚举实例的类 EnumSets 在同一个包下;import static chapter19.AlarmPoints.*; public class EnumSets {public static void main(String[] args) {EnumSet<AlarmPoints> points = EnumSet.noneOf(AlarmPoints.class); // EnumSet.add(BATHROOM) 把 BATHROOM 枚举实例 添加到 points EnumSet 容器points.add(BATHROOM);print("points.add(BATHROOM); points = " + points);// EnumSet.of(STAIR1, STAIR2, KITCHEN) 返回一个存储了 start1,start2,start3的新EnumSetpoints.addAll(EnumSet.of(STAIR1, STAIR2, KITCHEN));print("points.addAll(EnumSet.of(STAIR1, STAIR2, KITCHEN)); points = " + points);// EnumSet.allOf(AlarmPoints.class) 返回 包含 AlarmPoints类所有的枚举实例的新EnumSetpoints = EnumSet.allOf(AlarmPoints.class);points.removeAll(EnumSet.of(STAIR1, STAIR2, KITCHEN));print("points = EnumSet.allOf(AlarmPoints.class); "+ "points.removeAll(EnumSet.of(STAIR1, STAIR2, KITCHEN)); points = " + points);// EnumSet.range(OFFICE1, OFFICE4) 返回 包含 OFFICE1~OFFICE4(包括端点)的枚举实例的新EnumSetpoints.removeAll(EnumSet.range(OFFICE1, OFFICE4));print("points.removeAll(EnumSet.range(OFFICE1, OFFICE4)); points = " + points);// EnumSet.complementOf(points) 返回 points枚举实例集合 相对于 AlarmPoints枚举实例集合的的【补集】points = EnumSet.complementOf(points);print("points = EnumSet.complementOf(points); points = " + points);}} /*points.add(BATHROOM); points = [BATHROOM]points.addAll(EnumSet.of(STAIR1, STAIR2, KITCHEN)); points = [STAIR1, STAIR2, BATHROOM, KITCHEN]points = EnumSet.allOf(AlarmPoints.class); points.removeAll(EnumSet.of(STAIR1, STAIR2, KITCHEN)); points = [LOBBY, OFFICE1, OFFICE2, OFFICE3, OFFICE4, BATHROOM, UTILITY]points.removeAll(EnumSet.range(OFFICE1, OFFICE4)); points = [LOBBY, BATHROOM, UTILITY]points = EnumSet.complementOf(points); points = [STAIR1, STAIR2, OFFICE1, OFFICE2, OFFICE3, OFFICE4, KITCHEN]*/
【代码解说】 EnumSet 方法列表如下:
EnumSet.add(BATHROOM) 
把 BATHROOM 枚举实例 添加到 points EnumSet 容器
EnumSet.of(STAIR1, STAIR2, KITCHEN)  返回一个存储了 start1,start2,start3的新EnumSet
EnumSet.of(T... array)  EnumSet.f() 方法有很多重载版本;
EnumSet.allOf(AlarmPoints.class)  返回 包含 AlarmPoints类所有的枚举实例的新EnumSet
EnumSet.range(OFFICE1, OFFICE4)  返回 包含 OFFICE1~OFFICE4(包括端点)的枚举实例的新EnumSet
EnumSet.complementOf(points)  返回 points枚举实例集合 相对于 AlarmPoints枚举实例集合的的【补集】

4)EnumSet是基于long类型的,long有64位,而一个enum实例只需要一位 bit 表示其是否存在。 即EnumSet 最多可以存储 不多于64个 enum 实例。
5)如果EnumSet 存储的元素超过64个,怎么办?


【荔枝-如果EnumSet 存储的元素超过64个】

// 荔枝-如果EnumSet 存储的元素超过64个,EnumSet还是可以存储超过64个的 enum 枚举实例public class BigEnumSet {  enum Big { A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10,    A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21,    A22, A23, A24, A25, A26, A27, A28, A29, A30, A31, A32,    A33, A34, A35, A36, A37, A38, A39, A40, A41, A42, A43,    A44, A45, A46, A47, A48, A49, A50, A51, A52, A53, A54,    A55, A56, A57, A58, A59, A60, A61, A62, A63, A64, A65,    A66, A67, A68, A69, A70, A71, A72, A73, A74, A75 }  public static void main(String[] args) {    EnumSet<Big> bigEnumSet = EnumSet.allOf(Big.class);    System.out.println(bigEnumSet);  }} /* Output:[A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25, A26, A27, A28, A29, A30, A31, A32, A33, A34, A35, A36, A37, A38, A39, A40, A41, A42, A43, A44, A45, A46, A47, A48, A49, A50, A51, A52, A53, A54, A55, A56, A57, A58, A59, A60, A61, A62, A63, A64, A65, A66, A67, A68, A69, A70, A71, A72, A73, A74, A75]*///:~
【19.9】使用 EnumMap
1)EnumMap: 
要求键必须来自同一个 enum类的枚举实例,EnumMap在内部可以有数组实现;
// java.util.EnumMap 源码public class EnumMap<K extends Enum<K>, V> extends AbstractMap<K, V>    implements java.io.Serializable, Cloneable
【荔枝-EnumMap存储和操作枚举实例(这里采用了命令设计模式)】
// 命令接口interface Command {void action();}// 荔枝-EnumMap存储和操作枚举实例(这里采用了命令设计模式)public class EnumMaps {public static void main(String[] args) {EnumMap<AlarmPoints, Command> em = new EnumMap<AlarmPoints, Command>(AlarmPoints.class);em.put(KITCHEN, new Command() { // 匿名内部类public void action() {print("Kitchen fire!");}});em.put(BATHROOM, new Command() { // 匿名内部类public void action() {print("Bathroom alert!");}});// 遍历 EnumMapfor (Map.Entry<AlarmPoints, Command> e : em.entrySet()) {printnb(e.getKey() + ": ");e.getValue().action();}try { // If there's no value for a particular key:em.get(UTILITY).action(); // 如果get()方法中的键值不存在,则报空指针异常} catch (Exception e) {print(e);}}} /*BATHROOM: Bathroom alert!KITCHEN: Kitchen fire!java.lang.NullPointerException*/
【代码解说】
解说1) 与 EnumSet一样:
enum实例定义时的顺序决定了该enum实例在 EnumMap  中的存储顺序;
解说2) EnumMap的优点: 与常量相比, 常量在编译期就被固定了,是不能修改的;即使EnumMap的键无法修改但允许修改值对象(EnumMap的键固定而值可变);
解说3)多路分发: 可以利用 EnumMap 实现多路分发;

【19.10】常量相关的方法
1)java 枚举类机制: 
允许为 每个 enum 实例编写不同的抽象方法实现,从而赋予每个 enum 实例不同的行为;
2)如何实现常量相关的方法: 需要为 enum 定义一个或多个 abstract方法,然后为每个 enum 实例实现该抽象方法;


【荔枝-实现常量相关的方法(通过枚举类间接实现多态)】

// 荔枝-为每个 enum 实例编写不同的抽象方法实现(实现的方法,就是常量相关的方法,因为枚举类中的实例是 final 类型,这可以通过 javap 反编译 enum类来实现)public enum ConstantSpecificMethod {// 为 每个 enum 实例编写不同的抽象方法实现DATE_TIME {String getInfo() {return DateFormat.getDateInstance().format(new Date());}},CLASSPATH {String getInfo() {return System.getenv("CLASSPATH");}},VERSION {String getInfo() {return System.getProperty("java.version");}};abstract String getInfo(); // 抽象方法public static void main(String[] args) {// enum实例的 values() 和 valueof()方法是 编译器为 enum 实例添加的for (ConstantSpecificMethod csm : values())System.out.println(csm.getInfo());}}  /* 2017-12-21.;D:\java\jdk1.8.0_91\lib\tools.jar;D:\java\jdk1.8.0_91\lib\dt.jar;1.8.0_91*/
【注意】 请比较 ConstantSpecificMethod 与 命令设计模式 EnumMaps 类的相似之处;

3)无法将 enum 实例 作为一个类型来使用


【荔枝-无法将 enum 实例 作为一个类型来使用】

// 荔枝-无法将 enum 实例 作为一个类型来使用enum LikeClasses {WINKEN {void behavior() {print("Behavior1");}},BLINKEN {void behavior() {print("Behavior2");}},NOD {void behavior() {print("Behavior3");}};abstract void behavior();}public class NotClasses {// 无法使用 LikeClasses.WINKEN 来声明对象引用// void f1(LikeClasses.WINKEN instance) {} // Nope} 
/* 反编译结果如下: E:\bench-cluster\spring_in_action_eclipse\AThinkingInJava\src>javac -encoding utf-8 chapter19/NotClasses.javaE:\bench-cluster\spring_in_action_eclipse\AThinkingInJava\src>javap chapter19.NotClassesCompiled from "NotClasses.java"public class chapter19.NotClasses {  public chapter19.NotClasses();}E:\bench-cluster\spring_in_action_eclipse\AThinkingInJava\src>javap chapter19.LikeClassesCompiled from "NotClasses.java"abstract class chapter19.LikeClasses extends java.lang.Enum<chapter19.LikeClasses> {  public static final chapter19.LikeClasses WINKEN;  public static final chapter19.LikeClasses BLINKEN;  public static final chapter19.LikeClasses NOD;  public static chapter19.LikeClasses[] values();  public static chapter19.LikeClasses valueOf(java.lang.String);  abstract void behavior();  chapter19.LikeClasses(java.lang.String, int, chapter19.LikeClasses$1);  static {};}*/
【代码解说】
解说1)通过反编译的结果知: 
每个enum元素都是 LikeClasses 类型的 static final 实例;
解说2)因为它们是static实例,所以无法访问外部类的 非static 元素或方法: 所以对于内部的enum 实例来说,其行为与一般的内部类并不相同;

【荔枝-将一个常量相关的方法关联到选择上,再使用 EnumSet保存客户选择】
// 荔枝-将一个常量相关的方法关联到选择上,再使用 EnumSet保存客户选择public class CarWash {public enum Cycle { // enum 类UNDERBODY { // 每个enum实例编写不同的抽象方法实现// 常量相关的方法,因为 UNDERBODY 的类型是  public static final chapter19.CarWash$Cycle UNDERBODY; (static final 是常量类)void action() { print("Spraying the underbody");}},WHEELWASH {void action() { // 常量相关的方法print("Washing the wheels");}},PREWASH {void action() { print("Loosening the dirt");}},BASIC {void action() {  // 常量相关的方法print("The basic wash");}},HOTWAX {void action() { // 常量相关的方法print("Applying hot wax");}},RINSE {void action() { // 常量相关的方法print("Rinsing");}},BLOWDRY {void action() { // 常量相关的方法print("Blowing dry");}};abstract void action();}// EnumSet.of() 有很多重载方法,可以传入多个 enum实例进行存储EnumSet<Cycle> cycles = EnumSet.of(Cycle.BASIC, Cycle.RINSE);public void add(Cycle cycle) {cycles.add(cycle);}// 遍历 EnumSet 中的 enum 实例public void washCar() {for (Cycle c : cycles) c.action();}public String toString() {return cycles.toString();}public static void main(String[] args) {CarWash wash = new CarWash();print("wash = " + wash);print("\nwash.washCar() = ");wash.washCar(); // 遍历 EnumSet 中的 enum 实例// 元素被添加的顺序 并不决定其 存储顺序wash.add(Cycle.BLOWDRY);wash.add(Cycle.BLOWDRY); // 重复元素被忽略wash.add(Cycle.RINSE);wash.add(Cycle.HOTWAX);print("wash = " + wash);print("\nwash.washCar() = ");wash.washCar();}}  /*wash = [BASIC, RINSE]wash.washCar() = The basic washRinsingwash = [BASIC, HOTWAX, RINSE, BLOWDRY]wash.washCar() = The basic washApplying hot waxRinsingBlowing dry*/
/* 反编译结果: E:\bench-cluster\spring_in_action_eclipse\AThinkingInJava\src>javac -encoding utf-8 chapter19/CarWash.javaE:\bench-cluster\spring_in_action_eclipse\AThinkingInJava\src>javap chapter19.CarWash$CycleCompiled from "CarWash.java"public abstract class chapter19.CarWash$Cycle extends java.lang.Enum<chapter19.CarWash$Cycle> {  public static final chapter19.CarWash$Cycle UNDERBODY;  public static final chapter19.CarWash$Cycle WHEELWASH;  public static final chapter19.CarWash$Cycle PREWASH;  public static final chapter19.CarWash$Cycle BASIC;  public static final chapter19.CarWash$Cycle HOTWAX;  public static final chapter19.CarWash$Cycle RINSE;  public static final chapter19.CarWash$Cycle BLOWDRY;  public static chapter19.CarWash$Cycle[] values();  public static chapter19.CarWash$Cycle valueOf(java.lang.String);  abstract void action();  chapter19.CarWash$Cycle(java.lang.String, int, chapter19.CarWash$1);  static {};}*/
【代码解说】
解说1)
与使用匿名内部类相比较,定义常量相关的方法的语法更高效和简洁;
解说2)EnumSet 不存储重复的 enum 实例,所以对同一个 enum实例调用两次 add() 方法,大于等于第2次的add()方法调用都会被忽略;
解说3)向EnumSet 添加元素的顺序并不能决定其在 EnumSet中的存储顺序,其存储顺序 决定于 enum 实例定义时的顺序;

4)覆盖 enum实例的常量相关的方法;因为 enum实例的类型是 static final enum类型,静态常量枚举类型;


【荔枝-覆盖 enum实例的常量相关的方法】

// 荔枝-覆盖 enum实例的常量相关的方法public enum OverrideConstantSpecific {NUT, //  默认方法BOLT, //  默认方法 WASHER { // 重载方法// 这是赤裸裸的覆盖 常量相关的方法啊。@Overridevoid f() {print("Overridden method");}};void f() {print("default behavior");}public static void main(String[] args) {for (OverrideConstantSpecific ocs : values()) {printnb(ocs + ": ");ocs.f();}}} /*NUT: default behaviorBOLT: default behaviorWASHER: Overridden method*/
【19.10.1】基于enum的职责链(职责链设计模式)
1)职责链设计模式:
当一个请求到来时,它遍历这个链,直到链中的某个解决方法能够处理该请求;


【荔枝-使用enum实现职责链】

 /* 荔枝-使用enum实现职责链 */class Mail {  // The NO's lower the probability of random selection:  enum GeneralDelivery {YES,NO1,NO2,NO3,NO4,NO5} // 枚举实例序列  enum Scannability {UNSCANNABLE,YES1,YES2,YES3,YES4} // 枚举实例序列  enum Readability {ILLEGIBLE,YES1,YES2,YES3,YES4} // 枚举实例序列  enum Address {INCORRECT,OK1,OK2,OK3,OK4,OK5,OK6} // 枚举实例序列  enum ReturnAddress {MISSING,OK1,OK2,OK3,OK4,OK5} // 枚举实例序列    GeneralDelivery generalDelivery;  Scannability scannability;  Readability readability;  Address address;  ReturnAddress returnAddress;  static long counter = 0;  long id = counter++;   public String toString() { return "Mail " + id; }  public String details() {    return toString() +      ", General Delivery: " + generalDelivery +      ", Address Scanability: " + scannability +      ", Address Readability: " + readability +      ", Address Address: " + address +      ", Return address: " + returnAddress;  }  // 产生一个包含随机enum实例的Mail对象  public static Mail randomMail() {    Mail m = new Mail();    m.generalDelivery= Enums.random(GeneralDelivery.class);    m.scannability = Enums.random(Scannability.class);    m.readability = Enums.random(Readability.class);    m.address = Enums.random(Address.class);    m.returnAddress = Enums.random(ReturnAddress.class);    return m;  }  // 遍历 Mail 对象的迭代器(遍历下一个元素,通过调用randomMail() 来实现。 )  public static Iterable<Mail> generator(final int count) {    return new Iterable<Mail>() { // 匿名内部类      int n = count;      public Iterator<Mail> iterator() {        return new Iterator<Mail>() {          public boolean hasNext() { return n-- > 0; }          public Mail next() { return randomMail(); } // 产生一个包含随机enum实例的Mail对象          public void remove() { // Not implemented            throw new UnsupportedOperationException();          }        };      }    };  }}// PostOffice类内部 封装了多个枚举类public class PostOffice {  enum MailHandler {    GENERAL_DELIVERY {      @Override      boolean handle(Mail m) { // 3.这是重写 enum 常量相关的方法        switch(m.generalDelivery) {          case YES:            print("Using general delivery for " + m);            return true;          default: return false;        }      }    },    MACHINE_SCAN {    @Override      boolean handle(Mail m) { // 3.这是重写 enum 常量相关的方法        switch(m.scannability) {          case UNSCANNABLE: return false;          default:            switch(m.address) {              case INCORRECT: return false;              default:                print("Delivering "+ m + " automatically");                return true;            }        }      }    },    VISUAL_INSPECTION {    @Override      boolean handle(Mail m) { // 3.这是重写 enum 常量相关的方法        switch(m.readability) {          case ILLEGIBLE: return false;          default:            switch(m.address) {              case INCORRECT: return false;              default:                print("Delivering " + m + " normally");                return true;            }        }      }    },    RETURN_TO_SENDER {      @Override      boolean handle(Mail m) { // 3.这是重写 enum 常量相关的方法        switch(m.returnAddress) {          case MISSING: return false;          default:            print("Returning " + m + " to sender");            return true;        }      }    };    abstract boolean handle(Mail m); // 抽象方法  }  // 2.静态方法  static void handle(Mail m) {    for(MailHandler handler : MailHandler.values())    // 调用被覆盖的常量相关的方法,如果能够处理(返回true),则停止遍历职责链      if(handler.handle(m))         return;    print(m + " is a dead letter");  }  public static void main(String[] args) {    for(Mail mail : Mail.generator(10)) {      print(mail.details()); // 调用 Mail.toString() 方法      handle(mail); // 1.静态方法      print("*****");    }  }} /*Mail 0, General Delivery: NO2, Address Scanability: UNSCANNABLE, Address Readability: YES3, Address Address: OK1, Return address: OK1Delivering Mail 0 normally*****Mail 1, General Delivery: NO5, Address Scanability: YES3, Address Readability: ILLEGIBLE, Address Address: OK5, Return address: OK1Delivering Mail 1 automatically*****Mail 2, General Delivery: YES, Address Scanability: YES3, Address Readability: YES1, Address Address: OK1, Return address: OK5Using general delivery for Mail 2*****Mail 3, General Delivery: NO4, Address Scanability: YES3, Address Readability: YES1, Address Address: INCORRECT, Return address: OK4Returning Mail 3 to sender*****Mail 4, General Delivery: NO4, Address Scanability: UNSCANNABLE, Address Readability: YES1, Address Address: INCORRECT, Return address: OK2Returning Mail 4 to sender*****Mail 5, General Delivery: NO3, Address Scanability: YES1, Address Readability: ILLEGIBLE, Address Address: OK4, Return address: OK2Delivering Mail 5 automatically*****Mail 6, General Delivery: YES, Address Scanability: YES4, Address Readability: ILLEGIBLE, Address Address: OK4, Return address: OK4Using general delivery for Mail 6*****Mail 7, General Delivery: YES, Address Scanability: YES3, Address Readability: YES4, Address Address: OK2, Return address: MISSINGUsing general delivery for Mail 7*****Mail 8, General Delivery: NO3, Address Scanability: YES1, Address Readability: YES3, Address Address: INCORRECT, Return address: MISSINGMail 8 is a dead letter // Mail 8 对象是 职责链无法解决的。*****Mail 9, General Delivery: NO1, Address Scanability: UNSCANNABLE, Address Readability: YES2, Address Address: OK1, Return address: OK4Delivering Mail 9 normally******/
【代码解说】
解说1)
以上职责链由 enum MailHandler实现,而 enum定义的枚举实例次序决定了各个策略在应用时的次序;

【19.10.2】使用enum的状态机(状态设计模式:如成都各大高校中的米源自动售货机)
1)枚举类型非常适合用来创建状态机: 一个状态机有有限个状态。 
状态机通常根据输入,从一个状态转移到下一个状态。也有瞬时状态,一旦任务结束,状态机就离开瞬时状态;


【状态机荔枝-自动售货机】

// 状态机荔枝-自动售货机(枚举类和枚举实例)public enum Input {  NICKEL(5),   DIME(10),   QUARTER(25),   DOLLAR(100),  TOOTHPASTE(200),   CHIPS(75),   SODA(100),   SOAP(50),  ABORT_TRANSACTION {    public int amount() { // 重写常量相关的方法      throw new RuntimeException("ABORT.amount()");    }  },  STOP {     public int amount() { // 重写常量相关的方法      throw new RuntimeException("SHUT_DOWN.amount()");    }  };  int value; // In cents    Input(int value) { this.value = value; }  Input() {}    int amount() { return value; }; // In cents  static Random rand = new Random(47);  public static Input randomSelection() {    // Don't include STOP:    return values()[rand.nextInt(values().length - 1)];  }} ///:~
// 荔枝-自动贩卖机开始package chapter19;import java.io.File;import java.util.*;import net.mindview.util.*;import static chapter19.Input.*; // 静态导入,主要枚举实例无需枚举类修饰import static net.mindview.util.Print.*;enum Category {  // 枚举实例序列: MONEY是Category的枚举实例,而NICKEL等是 Input的枚举实例  MONEY(NICKEL, DIME, QUARTER, DOLLAR), // 把MONEY NICKEL, DIME, QUARTER, DOLLAR 作为 EnumMap的4个key,而把MONEY 作为值(4个键对应一个值)插入到EnumMap中  ITEM_SELECTION(TOOTHPASTE, CHIPS, SODA, SOAP),  QUIT_TRANSACTION(ABORT_TRANSACTION),  SHUT_DOWN(STOP);    private Input[] values;  Category(Input... types) { // 构造方法  values = types;  // 这里的values 就是插入到 EnumMap的values  }  // 使用 EnumMap 作为枚举实例的容器  private static EnumMap<Input,Category> categories = new EnumMap<Input,Category>(Input.class);  static { // 静态块    for(Category c : Category.class.getEnumConstants())      for(Input type : c.values)        categories.put(type, c); // 把Input枚举实例作为键 和 把Category枚举实例作为值插入到EnumMap中  }  public static Category categorize(Input input) {    return categories.get(input); // 在EnumMap中获取以input枚举实例作为键的 Category 值;  }}
// 荔枝-自动贩卖机public class VendingMachine {  public static final String path = System.getProperty("user.dir") + File.separator + "chapter19" + File.separator;   private static State state = State.RESTING;  private static int amount = 0;  private static Input selection = null;    enum StateDuration { TRANSIENT } // StateDuration 也是枚举类,其只有TRANSIENT 这一个枚举实例;   enum State { // State 枚举类声明开始    RESTING { // 枚举实例,并重写State枚举类的next()方法      void next(Input input) {    // Category.categorize(input): 在EnumMap中获取以input枚举实例作为键的 Category 值        switch(Category.categorize(input)) {          case MONEY:            amount += input.amount();            state = ADDING_MONEY;            break;          case SHUT_DOWN:            state = TERMINAL; // 停止          default:        }      }    },    ADDING_MONEY { // 枚举实例,并重写State枚举类的next()方法      void next(Input input) {        switch(Category.categorize(input)) {          case MONEY:            amount += input.amount();            break;          case ITEM_SELECTION:            selection = input;            if(amount < selection.amount())              print("Insufficient money for " + selection);            else state = DISPENSING;            break;          case QUIT_TRANSACTION:            state = GIVING_CHANGE;            break;          case SHUT_DOWN:            state = TERMINAL; // 停止          default:        }      }    },    // 调用 State 有参构造方法    DISPENSING(StateDuration.TRANSIENT) { // 枚举实例,并重写State枚举类的next()方法      void next() {        print("here is your " + selection);        amount -= selection.amount();        state = GIVING_CHANGE;      }    },    GIVING_CHANGE(StateDuration.TRANSIENT) { // 枚举实例,并重写State枚举类的next()方法      void next() {        if(amount > 0) {          print("Your change: " + amount);          amount = 0;        }        state = RESTING;      }    },    TERMINAL { void output() { print("Halted"); } }; // 枚举实例,并重写State枚举类的output()方法        private boolean isTransient = false; // 是否是瞬时状态    State() {} // State 无参构造方法    State(StateDuration trans) { isTransient = true; }  // State 有参构造方法(只有调用有参的State构造器,才会进入瞬时状态)        void next(Input input) { // 枚举实例不重写枚举类State的 next(Input input) 方法就抛异常      throw new RuntimeException("Only call " +        "next(Input input) for non-transient states");    }    void next() { // 枚举实例不重写枚举类State的 next() 方法就抛异常      throw new RuntimeException("Only call next() for " +        "StateDuration.TRANSIENT states");    }    void output() { print(amount); }  }// State 枚举类声明结束    static void run(Generator<Input> gen) { // VendingMachine 的静态方法    while(state != State.TERMINAL) {      state.next(gen.next()); // gen.next() 返回 Input 枚举类中名为 input.next()=文件中的下一个字符串 的Input类型的枚举实例      while(state.isTransient) //  只要state 不是 DISPENSING 或 GIVING_CHANGE 枚举实例,state.isTransient始终未false        state.next();      state.output(); // 只有 TERMINAL State枚举实例重写了 output() 方法    }  }  public static void main(String[] args) {    Generator<Input> gen = new RandomInputGenerator();    args = new String[]{"",""};        if(args.length == 1)      gen = new FileInputGenerator(path + "VendingMachineInput.txt");    run(gen);  }}// For a basic sanity check:class RandomInputGenerator implements Generator<Input> {  public Input next() { return Input.randomSelection(); }}// Create Inputs from a file of ';'-separated strings:class FileInputGenerator implements Generator<Input> {  private Iterator<String> input;    public FileInputGenerator(String fileName) { // 构造器    input = new TextFile(fileName, ";").iterator();  }    public Input next() { // 随机输入生成器的next()方法    if(!input.hasNext())      return null;    // Enum.valueOf(Input.class, input.next().trim()) 返回 Input 枚举类中名为 input.next()=文件中的下一个字符串 的Input类型的枚举实例    return Enum.valueOf(Input.class, input.next().trim());  }} /* Output: 也有可能一时半会停不下来,哈哈!!255075here is your CHIPS0100200here is your TOOTHPASTE02535Your change: 3502535Insufficient money for SODA35607075Insufficient money for SODA75Your change: 750Halted*///:~
/* VendingMachineInput.txt 下面是售货机的操作类型列表QUARTER; QUARTER; QUARTER; CHIPS;DOLLAR; DOLLAR; TOOTHPASTE;QUARTER; DIME; ABORT_TRANSACTION;QUARTER; DIME; SODA;QUARTER; DIME; NICKEL; SODA;ABORT_TRANSACTION;STOP;*/
【19.11】多路分发
1)如运算表达式 a.plus(b): 
a 和 b的数据类型都未知,如何让a和b参与运算呢?即要执行的操作包含了不止一个类型未知的对象,这里是有两个类型未知的对象(a和b),
而java动态绑定机制只能处理其中一种类型,这无法解决这个问题。所以必须自定义动态绑定行为;
2)解决上面的问题是多路分发: 如果要处理多个不同的类型体系,就需要为每个类型体系执行一个方法调用。

【荔枝-使用 enum 实现二路分发的荔枝(经典荔枝-基于enum的二路分发)】
// Outcome.java 源码package chapter19;public enum Outcome { WIN, LOSE, DRAW } // 赢,输,平局// Competitor.java 源码package chapter19;public interface Competitor<T extends Competitor<T>> {  Outcome compete(T competitor);} ///:~// RoShamBo1.java 源码package chapter19;import java.util.*;import static chapter19.Outcome.*; // 静态导入// 荔枝-使用 enum 实现二路分发的荔枝// Item 接口interface Item {  Outcome compete(Item it);  Outcome eval(Paper p);  Outcome eval(Scissors s);  Outcome eval(Rock r);}// Item 接口实现类class Paper implements Item {  // public enum Outcome { WIN, LOSE, DRAW } // 赢,输,平局 ( Outcome 枚举类型实例)  public Outcome compete(Item it) { return it.eval(this); } // 返回 Outcome 枚举类型实例  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"; }}//Item 接口实现类class Scissors implements Item {  public Outcome compete(Item it) { return it.eval(this); } // 返回 Outcome 枚举类型实例  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"; }}//Item 接口实现类class Rock implements Item {  public Outcome compete(Item it) { return it.eval(this); } // 返回 Outcome 枚举类型实例  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() { // 随机创建一个Item的实现类    switch(rand.nextInt(3)) { // 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)); // 相应的Item接口实现类的 compete()方法  }  public static void main(String[] args) {    for(int i = 0; i < SIZE; i++)      match(newItem(), newItem());  }} /* Output:Rock vs. Rock: DRAWPaper vs. Rock: WINPaper vs. Rock: WINPaper vs. Rock: WINScissors vs. Paper: WINScissors vs. Scissors: DRAWScissors vs. Paper: WINRock vs. Paper: LOSEPaper vs. Paper: DRAWRock vs. Paper: LOSEPaper vs. Scissors: LOSEPaper vs. Scissors: LOSERock vs. Scissors: WINRock vs. Paper: LOSEPaper vs. Rock: WINScissors vs. Paper: WINPaper vs. Scissors: LOSEPaper vs. Scissors: LOSEPaper vs. Scissors: LOSEPaper vs. Scissors: LOSE*///:~
【代码解说】
解说1)Item是这几种方法类型的接口,一共有三种分发类型,
所以你会看到 eval() 方法被重载了9次,因为3*3,每两种类型的比较就可以确定一个eval()的重载版本;
解说2)可以看到,在调用 a.compete(b) 时,a和b的类型都是 Item,但具体是什么类型未知。但是通过 a 和 b的组合就可以知道调用哪一个 compete() 和 eval() 重载版本的方法;
解说3)多路分发的好处:在于保持方法调用时的优雅语法,避免了在一个方法中判定多个对象的类型的丑陋代码;你只需要说:嘿,你们两个,我不在乎你们是什么类型,请自行交流;(不能再干货-基于enum和多态的多路分发荔枝)

【19.11.1】使用enum实现多路分发
1)使用构造器来初始化每个 enum 实例,并以一组结果作为参数;它们的组合就形成了类似查询表的结构:


【荔枝-使用enum实现多路分发】

// 荔枝-使用enum实现多路分发public enum RoShamBo2 implements Competitor<RoShamBo2> {  // enum实例序列,调用RoShamBo2的构造器  PAPER(DRAW, LOSE, WIN), //DRAW, LOSE, WIN分布赋值给 vPAPER, vSCISSORS, vROCK  SCISSORS(WIN, DRAW, LOSE), // 同理  ROCK(LOSE, WIN, DRAW); // 同理  private Outcome vPAPER, vSCISSORS, vROCK; // Outcome类型的枚举实例    // RoShamBo2枚举类的构造方法  RoShamBo2(Outcome paper,Outcome scissors,Outcome rock) {    this.vPAPER = paper; // 给Output实例赋值    this.vSCISSORS = scissors; // 给Output实例赋值    this.vROCK = rock; // 给Output实例赋值  }  // 重写 compete() 比较方法  @Override  public Outcome compete(RoShamBo2 it) {    switch(it) {      default:      case PAPER: return vPAPER;      case SCISSORS: return vSCISSORS;      case ROCK: return vROCK;    }  }  public static void main(String[] args) {    RoShamBo.play(RoShamBo2.class, 20);  }} /* Output:ROCK vs. ROCK: DRAWSCISSORS vs. ROCK: LOSESCISSORS vs. ROCK: LOSESCISSORS vs. ROCK: LOSEPAPER vs. SCISSORS: LOSEPAPER vs. PAPER: DRAWPAPER vs. SCISSORS: LOSEROCK vs. SCISSORS: WINSCISSORS vs. SCISSORS: DRAWROCK vs. SCISSORS: WINSCISSORS vs. PAPER: WINSCISSORS vs. PAPER: WINROCK vs. PAPER: LOSEROCK vs. SCISSORS: WINSCISSORS vs. ROCK: LOSEPAPER vs. SCISSORS: LOSESCISSORS vs. PAPER: WINSCISSORS vs. PAPER: WINSCISSORS vs. PAPER: WINSCISSORS vs. PAPER: WIN*///:~//RoShamBo.java 源码public class RoShamBo {  // 注意方法的基于泛型的返回值类型并比较  public static <T extends Competitor<T>> void match(T a, T b) {    System.out.println(a + " vs. " + b + ": " +  a.compete(b));  }  // 注意方法的基于泛型的返回值类型并比较  public static <T extends Enum<T> & Competitor<T>> void play(Class<T> rsbClass, int size) {    for(int i = 0; i < size; i++)      match(Enums.random(rsbClass),Enums.random(rsbClass));  }} ///:~// Enums.random() 方法源码public class Enums {private static Random rand = new Random(47);// 通过反射 Class.getEnumConstants() 来随机选取public static <T extends Enum<T>> T random(Class<T> ec) {// 通过 Class.getEnumConstants()方法取得所有 enum 实例return random(ec.getEnumConstants());}public static <T> T random(T[] values) {return values[rand.nextInt(values.length)]; // 随机选取}} // /:~
【代码解说】
解说1)
在compete() 分发中,只要a和b的类型确定下来,就可以找到唯一的case,从而返回执行结果;
解说2) match()方法的返回值类型是 <T extends Competitor<T>>;而play()方法的返回值类型是  <T extends Enum<T> & Competitor<T>> 注意是 <T extends Enum<T> 类型且 Competitor<T>类型;


【19.11.2】使用常量相关的方法实现多路分发
1)





阅读全文
'); })();
0 0
原创粉丝点击
热门IT博客
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 蓝奏云提取码 sds902.com dandy-850番号dandy-828 dandy-850 AndroidEditText禁止编辑 骚飘飘 dandy850 任务 关婷娜 d2c57e72-1e94-4a0e-b2b6-4500b4e76d48 JimNeath 淘淘 java商城 荒岛五姐妹切腹 赵丽颖 www.188740.com 鬼父 www.8x8x.com 网调惩罚 利用反射机制编写一个程序,这个程序能把类中所有的成员变量赋值,并把操作信息输出到控制台。 2024 54sds.com 42sds.cbm idea 编写一个程序sqlist.cpp,实现顺序表的各种基本运算(假设ElemType为char),并在此 编写一个程序sqlist.cpp,实现顺序表的各种基本运算并在此基础上设计一个程序exp2-1.cp 线性表的应用 上youtube https://www.csdndocs.com/focus/show/2424863/1 137*****177 母狗 myeclipse2022.1.0激活码 robocode 在线激活Licenseserver 酷狗音乐 安卓访问谷歌 图像金字塔 股票jjr 股票经纪人 StockbrokerGrapevine