java编程思想 第19章 枚举类型

来源:互联网 发布:电脑裁板锯用软件 编辑:程序博客网 时间:2024/06/10 22:38

java编程思想 第19章 枚举类型

Java SE5 添加了一个新的特性, enum 关键字. 用来声明枚举类型集. 可以代替 常量类型集,即类似功能的一组常量.

一,引入枚举

使用 enum 关键字,以前, 我们这样声明和使用常量集:

/**     *  声明一组常量集 fruits     */    public final static int APPLE = 0;    public final static int BANANA = 1;    public final static int PEAR = 2;    public final static int ORANGE = 3;    public final static int TOMATO = 4;    public static void main(String[] args) {        int fruitKind = TOMATO;        switch (fruitKind) {        case APPLE:            System.out.println("I want to eat apple");            break;        case BANANA:            System.out.println("I want to eat banana");            break;        case PEAR:            System.out.println("I want to eat pear");            break;        case ORANGE:            System.out.println("I want to eat orange");            break;        case TOMATO:            System.out.println("I want to eat tomato");            break;        default:            System.out.println("there is not any fruit");            break;        }    }
/**     *  enum 声明     * @author aloe     *     */    public enum Fruits{        APPLE, BANANA, PEAR, ORANGE, TOMATO    }

enum 声明的常量类型集,功能很强大,常量限定在一定范围内,很安全.

public static void main(String[] args) {// 使用 enum 就类似使用普通的 class,创建一个 enum 类型的引用,并给其赋值        Fruits oneFruit = Fruits.APPLE;        System.out.println(oneFruit);

打印结果:

APPLE

之所以会打印这样的结果,是因为:创建 enum 时,编译器会自动添加一些有用的属性, 可以理解为 Fruits 是 Enum.class 的一个实例.

这里写图片描述

图 1.1

因此 APPLE, BANANA, PEAR, ORANGE, TOMATO 都是 Enum.class 的实例,都具有这些属性和方法. 其中 toString() 的实现如下:

 private final String name;public String toString() {        return name;    }

所以可以很方便的显示 enum 实例的名字, 其中 ordinal() 返回的是 ordinal,是 每个 enum 常量的声明顺序序号,
values() 返回 enum 按常量声明的顺序的常量数组.

for (Fruits fruit : Fruits.values()) {            System.out.println(fruit + ".ordinal " + fruit.ordinal());        }

打印结果:

APPLE.ordinal 0BANANA.ordinal 1PEAR.ordinal 2ORANGE.ordinal 3TOMATO.ordinal 4

尽管 enum 看起来像是一种新的数据类型,但是这个关键字只是为 enum生成对应的类时,产生了某些编译行为,因此在很大程度上,你可以将 enum 当作其他任何类来处理.
switch 要在有限的可能集合中进行选择,因此与 enum配合使用,是绝佳的组合. enum 的名字可以倍加的展示程序意欲何为.

/**     *  enum 声明     * @author aloe     *     */    public enum Fruits{        APPLE, BANANA, PEAR, ORANGE, TOMATO    }    private Fruits oneFruit;    private EnumKeyword(Fruits oneFruit){        this.oneFruit = oneFruit;    }    private void choiceOneFruit(){        switch (oneFruit) {        case APPLE:            System.out.println("I want to eat apple");            break;        case BANANA:            System.out.println("I want to eat banana");            break;        case PEAR:            System.out.println("I want to eat pear");            break;        case ORANGE:            System.out.println("I want to eat orange");            break;        case TOMATO:            System.out.println("I want to eat tomato");            break;        default:            System.out.println("there is not any fruit");            break;        }    }    public static void main(String[] args) {        EnumKeyword enumKeyword = new EnumKeyword(Fruits.BANANA);        enumKeyword.choiceOneFruit();        }
I want to eat banana

二,基本的 enum 特性

创建 enum 时,编译器会生成一个 相关的类,上面例子中,就生成了一个 EnumKeyword$Fruits.class 这个 类是继承于 java.lang.Enum 的,所以 图1.1的 属性和方法, 生成的 Fruits.class 都有. 下图为 eclipse 编译生成的 class 文件

这里写图片描述

图1.2

enum 的 values() 方法,生成一个 Fruits 实例类型的数组,并且是按照 enum 生成时的顺序,所以 用, values()就可以遍历 enum实例.

for (Fruits fruit : Fruits.values()) {            //enum 实例的序列号,从0开始            System.out.println(fruit + " fruit.ordinal " + fruit.ordinal() + " enum 实例的序列号,从0开始 ");            //enum 的实例名字 和 toString() 返回的一样            System.out.println(fruit + " fruit.name " + fruit.name() + " enum 的实例名字 和 toString() 返回的一样 ");            System.out.print(fruit.compareTo(Fruits.PEAR) + " compareTo() 返回序列号的差 ");            System.out.print("||||");//          public final boolean equals(Object other) {//          return this==other;//      }            System.out.print(fruit.equals(Fruits.PEAR) + " equals() 用 == 比较的 ");            System.out.print("||||");            System.out.println(fruit == Fruits.PEAR);            System.out.println(fruit.getDeclaringClass() + " enum声明的类型 ");            System.out.println("====================================================");        }        System.out.println("====================================================");        System.out.println("====================================================");        // produce an enum value from a string name        for (String string : "APPLE BANANA PEAR ORANGE TOMATO".split(" ")) {            System.out.println(Enum.valueOf(Fruits.class, string));        }
PPLE fruit.ordinal 0 enum 实例的序列号,从0开始 APPLE fruit.name APPLE enum 的实例名字 和 toString() 返回的一样 -2 compareTo() 返回序列号的差 ||||false equals() 用 == 比较的 ||||falseclass com.crg.enumDemo.EnumKeyword$Fruits enum声明的类型 ====================================================BANANA fruit.ordinal 1 enum 实例的序列号,从0开始 BANANA fruit.name BANANA enum 的实例名字 和 toString() 返回的一样 -1 compareTo() 返回序列号的差 ||||false equals() 用 == 比较的 ||||falseclass com.crg.enumDemo.EnumKeyword$Fruits enum声明的类型 ====================================================PEAR fruit.ordinal 2 enum 实例的序列号,从0开始 PEAR fruit.name PEAR enum 的实例名字 和 toString() 返回的一样 0 compareTo() 返回序列号的差 ||||true equals() 用 == 比较的 ||||trueclass com.crg.enumDemo.EnumKeyword$Fruits enum声明的类型 ====================================================ORANGE fruit.ordinal 3 enum 实例的序列号,从0开始 ORANGE fruit.name ORANGE enum 的实例名字 和 toString() 返回的一样 1 compareTo() 返回序列号的差 ||||false equals() 用 == 比较的 ||||falseclass com.crg.enumDemo.EnumKeyword$Fruits enum声明的类型 ====================================================TOMATO fruit.ordinal 4 enum 实例的序列号,从0开始 TOMATO fruit.name TOMATO enum 的实例名字 和 toString() 返回的一样 2 compareTo() 返回序列号的差 ||||false equals() 用 == 比较的 ||||falseclass com.crg.enumDemo.EnumKeyword$Fruits enum声明的类型 ============================================================================================================================================================APPLEBANANAPEARORANGETOMATO

三,enum 的静态导入

package com.crg.staticimport;public enum Fruits {    APPLE, BANANA, PEAR, ORANGE, TOMATO}
package com.crg.staticimport;import static com.crg.staticimport.Fruits.*;public class TestStaticImport {    private Fruits fruit;    private TestStaticImport(Fruits fruit){        this.fruit = fruit;    }    @Override    public String toString() {        return fruit.name();    }    public static void main(String[] args) {        System.out.println(new TestStaticImport(APPLE));        System.out.println(new TestStaticImport(ORANGE));        System.out.println(new TestStaticImport(PEAR));        System.out.println(new TestStaticImport(BANANA));        System.out.println(new TestStaticImport(TOMATO));    }}
APPLEORANGEPEARBANANATOMATO

使用 import static 能够将 enum的实例标识符带入当前的命名空间,所以无需再用 enum类型来修饰 enum 实例. 这是 隐式的使用 enum 的实例,有利有弊,编译器自然可以确保使用正确的类型,程序不容易理解.

四, 向 enum 中添加新的方法

enum 除了不能继承于一个 enum 外,可以将 enum看做一个常规的类.我们可以向 enum种添加 方法,enum甚至可以有main()方法.我们可能希望每个枚举实例可以返回自己的描述,而 toString() 只能返回 实例的名称.我们可以通过添加一个构造器,专门处理这个额外的信息,添加一个方法,返回这个额外的信息.

package com.crg.staticimport;public enum FruitsAddAdditonalInfo {    /**     *  枚举实例 必须被定义在方法的前面     */    APPLE("这是苹果"),     BANANA("这是香蕉"),     PEAR("这是梨子"),     ORANGE("这是橘子"),     // 最后一个枚举实例后面必须添加一个";"    TOMATO("这是西红柿");    private String description;    /**     *  构造器的访问权限必须是 private 或者 package     * @param description     */    private FruitsAddAdditonalInfo(String description){        this.description = description;    }    public String getDescription(){        return description;    }    @Override    public String toString() {        // TODO Auto-generated method stub        return name() + " : " + getDescription();    }    public static void main(String[] args) {        for (FruitsAddAdditonalInfo fruit : FruitsAddAdditonalInfo.values()) {            System.out.println(fruit + " : " + fruit.getDescription());        }    }}
APPLE : 这是苹果 : 这是苹果BANANA : 这是香蕉 : 这是香蕉PEAR : 这是梨子 : 这是梨子ORANGE : 这是橘子 : 这是橘子TOMATO : 这是西红柿 : 这是西红柿

注意: 定义自己的方法,必须在enum实例的最后添加分号,必须先定义 enum实例,然后才能定义属性和方法,否则编译器会报错;上面的代码中,我们覆盖了 toString()方法 上例的 构造器的访问权限是 private,对于它的可访问性而言,并没有什么变化,因为构造器只能在enum的内部使用. 一旦enum定义结束,编译器就不允许我们再使用构造器创建任何手机实例.

五, values() 的神秘之处

上面的例子已经看到, enum类继承自 Enum类,但是 Enum类里面并没有 values() 方法.那么 values() 从哪里来的呢? 我们利用发射写一个小的测试程序.

package com.crg.enumDemo;import java.lang.reflect.Method;import java.lang.reflect.Type;import java.util.Set;import java.util.TreeSet;import com.crg.enumDemo.EnumKeyword.Fruits;public class TheSecretOfValues {    public static Set<String> analyze(Class<?> enumClass){        System.out.println("-----------analyzing " + enumClass.getSimpleName() +" ----------------");        //打印该类的接口        System.out.print(enumClass.getSimpleName() + "'s interfaces: ");        for (Type type : enumClass.getGenericInterfaces()) {            System.out.print(type + "; ");        }        System.out.println();        //打印该类的父类        System.out.println("Base: " + enumClass.getSuperclass());        //打印该类的所有方法名        System.out.println("Methods: ");        Set<String> methods = new TreeSet<>();        for (Method method : enumClass.getMethods()) {            methods.add(method.getName());        }        System.out.println(methods);        return methods;    }    public static void main(String[] args) {        // 获得 Enum.class 的所有方法        Set<String> enumMethods = analyze(Enum.class);        // 获得 Fruits.class 的所有方法        Set<String> fruitMethods = analyze(Fruits.class);        System.out.println("----------------------------------------------------");        System.out.println("fruitMethods.containsAll(enumMethods) ? : " + fruitMethods.containsAll(enumMethods));        System.out.println("fruitMethods.removeAll(enumMethods) >>>>>>>>>>>>>>>>>>");        fruitMethods.removeAll(enumMethods);        System.out.println("fruitMethods 移除enumMethods后的方法为: ");        //fruitMethods 移除enumMethods后的方法为:        System.out.println(fruitMethods);    }}
-----------analyzing Fruits ----------------Fruits's interfaces: Base: class java.lang.EnumMethods: [compareTo, equals, getClass, getDeclaringClass, hashCode, name, notify, notifyAll, ordinal, toString, valueOf, values, wait]----------------------------------------------------fruitMethods.containsAll(enumMethods) ? : truefruitMethods.removeAll(enumMethods) >>>>>>>>>>>>>>>>>>fruitMethods 移除enumMethods后的方法为: [values]

我们用 javap -private Fruits 反编译 Fruits.class 得到:

Compiled from "Fruits.java"public final class com.crg.staticimport.Fruits extends java.lang.Enum{    public static final com.crg.staticimport.Fruits APPLE;    public static final com.crg.staticimport.Fruits BANANA;    public static final com.crg.staticimport.Fruits PEAR;    public static final com.crg.staticimport.Fruits ORANGE;    public static final com.crg.staticimport.Fruits TOMATO;    private static final com.crg.staticimport.Fruits[] ENUM$VALUES;    static {};    private com.crg.staticimport.Fruits(java.lang.String, int);    public static com.crg.staticimport.Fruits[] values();    public static com.crg.staticimport.Fruits valueOf(java.lang.String);}

所以可以看出来:
values() 是编译器添加的静态方法; 并且编译器还添加了 valueOf(java.lang.String) 一个参数的方法,继承 java.lang.Enum的 valueOf(Class enumType, String name) 是两个参数的方法.还可以看到,用 final 修饰 Fruits, 所以 Fruits 不能被继承.

values() 是编译器 为 Fruits 添加的方法, Enum 类并没有此方法, 把 Fruits 向上转型为 Enum,就不能使用 values() 方法了.但是可以使用 Class.getEnumConstants() 获得 Fruits 的实例.

Fruits[] fruits = Fruits.values();        // Upcast 向上转型        Enum fruit = Fruits.APPLE;        for (Enum enumFruit : fruit.getClass().getEnumConstants()) {            System.out.println(enumFruit);        }
APPLEBANANAPEARORANGETOMATO

getEnumConstants() 只针对枚举类,如果非枚举类,调用此方法,将会发生异常

Class<Integer> integerClass = Integer.class;        for (Object obj : integerClass.getEnumConstants()) {            System.out.println(obj);        }
Exception in thread "main" java.lang.NullPointerException    at com.crg.enumDemo.TheSecretOfValues.main(TheSecretOfValues.java:62)

此时 getEnumConstants() 返回 null ,调用 null的 toString()就会抛出 NullPointerException.
从上面反编译出来的Fruits 代码可以看出来, Fruits 继承 Enum ,所以就不能再继承其他类了,但是可以实现.

package com.crg.staticimport;import java.util.Random;public enum Fruits implements Generator<Fruits>{    APPLE, BANANA, PEAR, ORANGE, TOMATO;    @Override    public Fruits next() {        Random random = new Random();        return values()[random.nextInt(values().length)];    }}
package com.crg.staticimport;public interface Generator<T> {    T next();}
public static <T> void PrintNext(Generator<T> generator){        System.out.print(generator.next() + " ");    }    public static void main(String[] args) {        Fruits generator = Fruits.BANANA;        for (int i = 0; i < 10; i++) {            PrintNext(generator);        }
BANANA ORANGE BANANA APPLE PEAR ORANGE ORANGE APPLE APPLE PEAR 

实现一个类似上面的随机产生一个 Enum 实例的 功能:

package com.crg.staticimport;import java.util.Random;public enum Fruits implements Generator<Fruits>{    APPLE, BANANA, PEAR, ORANGE, TOMATO;    @Override    public Fruits next() {        Random random = new Random();        return values()[random.nextInt(values().length)];    }    /**     *  随机产生一个 Enum 的实例     * @param ls     * @return     */    public static <T extends Enum<T>> T randomEnum(Class<T> ls){        return random(ls.getEnumConstants());    }    /**     *  随机产生一个 values 的数组元素     * @param values     * @return     */    public static <T> T random(T[] values){        Random random = new Random();        return values[random.nextInt(values.length)];    }    /**     *  重写该方法     */    @Override    public String toString() {        return name() + " || ";    }}
for (int i = 0; i < 15; i++) {            System.out.print(Fruits.randomEnum(Fruits.class));        }
PEAR || BANANA || TOMATO || ORANGE || PEAR || BANANA || PEAR || PEAR || TOMATO || TOMATO || ORANGE || PEAR || BANANA || BANANA || BANANA || 

六, 使用接口组织枚举

enum 不能被继承,也就不能扩展 enum 中的元素,如果我们希望,对enum 中的元素实现分组,可以通过 在一个接口内部,创建实现接口的枚举,来实现分组的目的.

package com.crg.enuminterface;public interface Food {    // 油泼面,裤袋面,扯面    enum Noodle implements Food {POUPO_NOODLE, POCKET_NOODLE, PULLED_NOODLE}    // 盖浇饭,黄焖鸡,炒饭    enum Rice implements Food {GAIJIAO_RICE, BRAISED_CHICKEN_RICE, FRIED_RICE}    // 蔬菜类:西红柿,土豆,西兰花       enum Vegetables implements Food {TOMATO, POTATO, BROCCOLI}}

对enum而言,实现接口,是实现子类化的唯一方式,上面的例子,每个 enum 都实现了 Food 接口,可以向上转型,所有的东西都是一种食物.

package com.crg.enuminterface;import static com.crg.enuminterface.Food.*;public class TypeOfFood {    public static void main(String[] args) {        Food food = Noodle.POUPO_NOODLE;        food = Rice.GAIJIAO_RICE;        food = Vegetables.BROCCOLI;    }}

如果要和一大堆类打交道,接口就不如enum好用了,例如一份包括上面食物Food的菜谱,使用 “枚举的枚举”,就很方便.

package com.crg.enuminterface;import java.util.Random;public enum Menu {    NOODLE(Food.Noodle.class),    RICE(Food.Rice.class),    VEGETABLES(Food.Vegetables.class);    private Food[] values;    private Menu(Class<? extends Food> kind){        values = kind.getEnumConstants();    }    /**     *  随机产生一个 values 的数组元素     * @param values     * @return     */    public static <T> T random(T[] values){        Random random = new Random();        return values[random.nextInt(values.length)];    }    /**     *  随机点一个食物     * @return     */    public Food randomSelect(){        return random(values);    }    // 生成5份随机的菜单    public static void main(String[] args) {        for (int i = 0; i < 5; i++) {            for (Menu menu : Menu.values()) {                System.out.println(menu.randomSelect());            }            System.out.println("=================================");        }    }}
POUPO_NOODLEFRIED_RICEBROCCOLI=================================POCKET_NOODLEFRIED_RICEPOTATO=================================POUPO_NOODLEGAIJIAO_RICEBROCCOLI=================================POUPO_NOODLEGAIJIAO_RICETOMATO=================================POCKET_NOODLEFRIED_RICEBROCCOLI=================================

上面这个例子就是获取”枚举的枚举”的例子.
还可以重新组织代码,使代码有更清晰的结构,enum 内部嵌套另一个 enum:

package com.crg.enuminterface;import java.util.Random;public enum MenuEnum {    NOODLE(Food.Noodle.class),    RICE(Food.Rice.class),    VEGETABLES(Food.Vegetables.class);    private Food[] values;    private MenuEnum(Class<? extends Food> kind){        values = kind.getEnumConstants();    }    /**     *  随机产生一个 values 的数组元素     * @param values     * @return     */    public static <T> T random(T[] values){        Random random = new Random();        return values[random.nextInt(values.length)];    }    /**     *  随机点一个食物     * @return     */    public Food randomSelect(){        return random(values);    }    interface Food {        // 油泼面,裤袋面,扯面        enum Noodle implements Food {POUPO_NOODLE, POCKET_NOODLE, PULLED_NOODLE}        // 盖浇饭,黄焖鸡,炒饭        enum Rice implements Food {GAIJIAO_RICE, BRAISED_CHICKEN_RICE, FRIED_RICE}        // 蔬菜类:西红柿,土豆,西兰花           enum Vegetables implements Food {TOMATO, POTATO, BROCCOLI}    }    // 生成5份随机的菜单    public static void main(String[] args) {        for (int i = 0; i < 5; i++) {            for (MenuEnum menu : MenuEnum.values()) {                System.out.println(menu.randomSelect());            }            System.out.println("=================================");        }    }}

七, EnumSet

set 集合不能向其中添加重复的元素, enum 实例也具有唯一性,这一点,两者很相似,但是 enum 不能实现实例的添加和删除,JAVA SE5 引入 EnumSet, EnumSet 的设计考虑到了速度的因素,因此用在判断一个 二进制位是否存在时,非常高效.
EnumSet 的元素必须来源 enum, 下面是一个学校 各个教室响铃的安装位置:

package com.crg.testenumset;import java.util.EnumSet;import static com.crg.testenumset.TestEnumSet.AlarmPoints.*;public class TestEnumSet {    enum AlarmPoints{classroom1, classroom2, classroom3, classroom4, classroom5, classroom6}    public static void main(String[] args) {        // 创建一个空的 EnumSet        EnumSet<AlarmPoints> points = EnumSet.noneOf(AlarmPoints.class);        points.add(classroom1);        System.out.println(points);        // 添加多个元素        points.addAll(EnumSet.of(classroom2, classroom3, classroom4));        System.out.println(points);        // 添加所有元素        points = EnumSet.allOf(AlarmPoints.class);        System.out.println(points);        // 移除指定所有元素        points.removeAll(EnumSet.of(classroom1, classroom2, classroom3));        System.out.println(points);        //添加单个元素        points.add(classroom1);        points.add(classroom2);        System.out.println(points);        // 保留指定范围内的所有元素        points.retainAll(EnumSet.range(classroom1, classroom4));        System.out.println(points);        // 移除指定范围内的所有元素        points.removeAll(EnumSet.range(classroom1, classroom4));        System.out.println(points);        // 产生一个新的EnumSet 包含所有元素,去处指定的元素        points = EnumSet.complementOf(EnumSet.of(classroom1));        System.out.println(points);    }}

EnumSet 方法的名字都相当直观,可以参考 API 点击查看API内容

EnumSet 的基础是 long,long是64位,当 元素超过64时,会再添加一个long.

八, 使用 EnumMap

EnumMap 是一种 特殊的 Map ,要求其中的键(key)必须来自 enum,EnumMap 内部用数组实现,因此 查找速度非常快,下面的实例使用enum实例调用put()方法,其他的使用和普通的Map类似.下面这个例子使用命令设计模式的用法使用 EnumMap

package com.crg.testenummap;import java.util.EnumMap;import java.util.Map;interface Command {    void action();}enum AlarmPoints {    classroom1, classroom2, classroom3, classroom4, classroom5, classroom6}public class TestEnumMap {    public static void main(String[] args) {        EnumMap<AlarmPoints, Command> en = new EnumMap<>(AlarmPoints.class);        en.put(AlarmPoints.classroom1, new Command() {            @Override            public void action() {                System.out.println("this is classroom1");            }        });        en.put(AlarmPoints.classroom1, new Command() {            @Override            public void action() {                System.out.println("this is classroom1");            }        });        en.put(AlarmPoints.classroom2, new Command() {            @Override            public void action() {                System.out.println("this is classroom2");            }        });        en.put(AlarmPoints.classroom3, new Command() {            @Override            public void action() {                System.out.println("this is classroom3");            }        });        for (Map.Entry<AlarmPoints, Command> e : en.entrySet()) {            System.out.println(e.getKey());            e.getValue().action();        }        // EnumMap 里没有这个key        en.get(AlarmPoints.classroom4).action();    }}
classroom1this is classroom1classroom2this is classroom2classroom3this is classroom3Exception in thread "main" java.lang.NullPointerException    at com.crg.testenummap.TestEnumMap.main(TestEnumMap.java:50)

九, 常量相关的方法

Java enum 有一个有趣的特性,允许程序员为 enum 编写方法,使每个enum实例拥有不同的行为,用 abstract 在enum里声明一个抽象方法,然后为每个enum 常量实现该抽象方法.

package com.crg.constant;import java.text.DateFormat;import java.util.Date;public enum ConstantSpecificMethod {    DATE_TIME{        @Override        String getInfo() {            return DateFormat.getDateInstance().format(new Date());        }},    CLASSPATH{        @Override        String getInfo() {            return System.getenv("CLASSPATH");        }        },    VERSION{        @Override        String getInfo() {            return System.getProperty("java.version");        }        };    abstract String getInfo();    public static void main(String[] args) {        for (ConstantSpecificMethod constantSpecificMethod : ConstantSpecificMethod.values()) {            System.out.println(constantSpecificMethod.getInfo());        }    }}
Nov 7, 2016null1.8.0_92

在这个例子中,每个enum实例,在 调用 getInfo() 具有不同的行为,体现出了多态的行为.enum实例与类的相似之处仅限于此了.并不能把enum实例当做类来使用.
再看一个例子:

package com.crg.constant;import java.util.EnumSet;public class TestConstant {    EnumSet<ConstantSpecificMethod> enumSet = EnumSet.of(ConstantSpecificMethod.CLASSPATH);    private void addConstant(ConstantSpecificMethod constantSpecificMethod){        enumSet.add(constantSpecificMethod);    }    private void PrinterInfo(){        for (ConstantSpecificMethod constantSpecificMethod : enumSet) {            System.out.println(constantSpecificMethod.getInfo());        }    }    public static void main(String[] args) {        TestConstant testConstant = new TestConstant();        testConstant.addConstant(ConstantSpecificMethod.VERSION);        testConstant.addConstant(ConstantSpecificMethod.DATE_TIME);        testConstant.addConstant(ConstantSpecificMethod.CLASSPATH);        testConstant.addConstant(ConstantSpecificMethod.VERSION);        testConstant.PrinterInfo();    }}
Nov 7, 2016null1.8.0_92

从上面这个例子可以看出 EnumSet 的一些特性:
因为是 set集合,元素不能重复,多次,add()同一个实例,EnumSet 会忽略掉;
EnumSet 保存的 enum实例的顺序和添加的先后没有关系,其顺序和 enum 实例声明的顺序一致.
enum 也可以覆盖常量一般的方法:

package com.crg.constant;import java.text.DateFormat;import java.util.Date;public enum ConstantSpecificMethod {    DATE_TIME{        @Override        String getInfo() {            return DateFormat.getDateInstance().format(new Date());        }        @Override        public void sayHello() {            // TODO Auto-generated method stub            super.sayHello();            System.out.println("DATE_TIME say hello");        }    },    CLASSPATH{        @Override        String getInfo() {            return System.getenv("CLASSPATH");        }        },    VERSION{        @Override        String getInfo() {            return System.getProperty("java.version");        }        };    /**     *  抽象方法     * @return     */    abstract String getInfo();    /**     *  一般方法     */    public void sayHello(){        System.out.println("say hello");    }    public static void main(String[] args) {        for (ConstantSpecificMethod constantSpecificMethod : ConstantSpecificMethod.values()) {            System.out.println(constantSpecificMethod.getInfo());        }    }}
package com.crg.constant;import java.util.EnumSet;public class TestConstant {    EnumSet<ConstantSpecificMethod> enumSet = EnumSet.of(ConstantSpecificMethod.CLASSPATH);    private void addConstant(ConstantSpecificMethod constantSpecificMethod){        enumSet.add(constantSpecificMethod);    }    private void PrinterInfo(){        for (ConstantSpecificMethod constantSpecificMethod : enumSet) {            System.out.println(constantSpecificMethod.getInfo());            constantSpecificMethod.sayHello();        }    }    public static void main(String[] args) {        TestConstant testConstant = new TestConstant();        testConstant.addConstant(ConstantSpecificMethod.VERSION);        testConstant.addConstant(ConstantSpecificMethod.DATE_TIME);        testConstant.addConstant(ConstantSpecificMethod.CLASSPATH);        testConstant.addConstant(ConstantSpecificMethod.VERSION);        testConstant.PrinterInfo();    }}
Nov 7, 2016say helloDATE_TIME say hellonullsay hello1.8.0_92say hello

十, 使用enum 的职责链

在职责链(Chain of Responsibility)的设计模式中,程序员以多种不同的方式解决同一个问题,然后把他们链接在一起.当一个请求到来时,它遍历这个链,直到链中的某个解决方案能够处理该请求.

下面的例子是利用enum常量的相关的方法,和责任链设计模式,实现的一个简单的责任链.
以邮局发送邮件为模型,邮局处理每个邮件使用通用模式,不断的尝试处理邮件,直到邮件被确认为 是死邮件;每一次尝试可以看做一个策略,而完整的处理方式,就是一个责任链.

package com.crg.mail;import java.util.Iterator;import java.util.Random;/** *  邮件对象 * @author aloe * */public class Mail {    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() + " |GeneralDelivery: " + generalDelivery                + " |Scannability: " + scannability                + " |Readability: " + readability                + " |Address: " + address                + " |ReturnAddress: " + returnAddress;    }    /**     * generate test Mail     * @return     */    public static Mail randomMail(){        Mail mail = new Mail();        mail.generalDelivery = randomEnum(GeneralDelivery.class);        mail.scannability = randomEnum(Scannability.class);        mail.readability = randomEnum(Readability.class);        mail.address = randomEnum(Address.class);        mail.returnAddress = randomEnum(ReturnAddress.class);        return mail;    }    /**     *  随机产生一个 Enum 的实例     * @param ls     * @return     */    public static <T extends Enum<T>> T randomEnum(Class<T> ls){        return random(ls.getEnumConstants());    }    /**     *  随机产生一个 values 的数组元素     * @param values     * @return     */    public static <T> T random(T[] values){        Random random = new Random();        return values[random.nextInt(values.length)];    }    public static Iterable<Mail> generator(final int count){        return new Iterable<Mail>() {            int n = count;            @Override            public Iterator<Mail> iterator() {                // TODO Auto-generated method stub                return new Iterator<Mail>() {                    @Override                    public boolean hasNext() {                        // TODO Auto-generated method stub                        return n-- > 0;                    }                    @Override                    public Mail next() {                        // TODO Auto-generated method stub                        return randomMail();                    }                    @Override                    public void remove() {                        // TODO Auto-generated method stub                        throw new UnsupportedOperationException();                    }                };            }        };    }}
package com.crg.mail;public class PostOffice {    enum MailHandler {        GENERAL_DELIVERY{            @Override            boolean handle(Mail mail) {                switch (mail.generalDelivery) {                case YES:                    System.out.println("using general delivery for " + mail);                    return true;                default:                    return false;                }            }},        MACHINE_SCAN {            @Override            boolean handle(Mail mail) {                switch (mail.scannability) {                case UNSCANNABLE:                    return false;                default:                    switch (mail.address) {                    case INCORRECT:                        return false;                    default:                        System.out.println("delivering " + mail + " automatically");                        return true;                    }                }            }            },        VISUAL_INSPECTION {            @Override            boolean handle(Mail mail) {                switch (mail.readability) {                case ILLEGIBLE:                    return false;                default:                    switch (mail.address) {                    case INCORRECT:                        return false;                    default:                        System.out.println("delivering " + mail + " normally");                        return true;                    }                }            }            },        RETURN_TO_SENDER {            @Override            boolean handle(Mail mail) {                switch (mail.returnAddress) {                case MISSING:                    return false;                default:                    System.out.println("returning " + mail + " to sender");                    return true;                }            }            };        abstract boolean handle(Mail mail);    }    static void handleMail(Mail mail){        for (MailHandler mailHandler : MailHandler.values()) {            if (!mailHandler.handle(mail)) {                System.out.println(mail + " is a dead letter");                return;            }        }    }    public static void main(String[] args) {        // TODO Auto-generated method stub        for (Mail mail : Mail.generator(10)) {            System.out.println(mail.details());            handleMail(mail);            System.out.println("==================================================");        }    }}
Mail 0 |GeneralDelivery: YES |Scannability: YES1 |Readability: YES4 |Address: OK6 |ReturnAddress: OK4using general delivery for Mail 0delivering Mail 0 automaticallydelivering Mail 0 normallyreturning Mail 0 to sender==================================================Mail 1 |GeneralDelivery: NO2 |Scannability: YES3 |Readability: YES3 |Address: OK3 |ReturnAddress: MISSINGMail 1 is a dead letter==================================================Mail 2 |GeneralDelivery: NO5 |Scannability: YES4 |Readability: ILLEGIBLE |Address: INCORRECT |ReturnAddress: OK3Mail 2 is a dead letter==================================================Mail 3 |GeneralDelivery: NO2 |Scannability: YES1 |Readability: YES2 |Address: INCORRECT |ReturnAddress: OK5Mail 3 is a dead letter==================================================Mail 4 |GeneralDelivery: NO2 |Scannability: YES2 |Readability: YES1 |Address: OK1 |ReturnAddress: OK3Mail 4 is a dead letter==================================================Mail 5 |GeneralDelivery: NO2 |Scannability: YES1 |Readability: YES2 |Address: OK2 |ReturnAddress: OK2Mail 5 is a dead letter==================================================Mail 6 |GeneralDelivery: NO4 |Scannability: YES1 |Readability: YES2 |Address: OK1 |ReturnAddress: OK1Mail 6 is a dead letter==================================================Mail 7 |GeneralDelivery: NO2 |Scannability: YES4 |Readability: YES1 |Address: OK1 |ReturnAddress: OK1Mail 7 is a dead letter==================================================Mail 8 |GeneralDelivery: NO5 |Scannability: YES3 |Readability: YES1 |Address: OK3 |ReturnAddress: OK3Mail 8 is a dead letter==================================================Mail 9 |GeneralDelivery: NO3 |Scannability: YES4 |Readability: ILLEGIBLE |Address: OK4 |ReturnAddress: OK5Mail 9 is a dead letter==================================================

责任链由 enum MailHandler 实现,enum 的定义次序决定了各个解决策略在应用时的次序.对每一封邮件都按该次序尝试每一个解决策略,如果有一个策略,失败,该邮件被判定为死件.

==============================================================================
上面的代码下载地址,请点击打开:

0 0
原创粉丝点击