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 的一个实例.
因此
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 文件
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 的定义次序决定了各个解决策略在应用时的次序.对每一封邮件都按该次序尝试每一个解决策略,如果有一个策略,失败,该邮件被判定为死件.
==============================================================================
上面的代码下载地址,请点击打开:
- java编程思想 第19章 枚举类型
- Java编程思想笔记——第19章枚举类型
- Java编程思想-19枚举类型
- 《Java 编程思想》--第十九章:枚举类型
- java编程思想读书笔记 第十九章 枚举类型
- 枚举类型 Java编程思想 读书笔记
- Java编程思想之枚举类型
- JAVA编程思想:第11章 运行期类型鉴定
- Java编程思想阅读笔记之--枚举类型
- 《java编程思想》第十九章 枚举
- JAVA编程思想第4版第14章类型信息的练习26
- java编程思想第4版第15章自限定类型笔记
- Thinking in Java 第19章 枚举类型
- 《Java 编程思想》--第十四章:类型信息
- 《java编程思想》第十四章 类型信息
- java编程思想(第四版)_第11章 运行期类型鉴定
- java编程思想学习_第14章_类型信息
- JAVA编程思想:第7章 多形性
- 一句话概括下spring框架及spring cloud框架主要组件
- 第九周ASCII比较
- MySQL 联合索引详解
- TCP连接的状态详解以及故障排查
- Oracle AWR 报告的生成和分析
- java编程思想 第19章 枚举类型
- c#中new()约束符
- KOOCAN非正常影视排行榜之遗忘的优秀中国动画
- 一次由于内存问题程序被kill的测试 (5)
- Http协议和tomcat目录个人总结
- string 与char* char[]之间的转换
- 【PAT甲级】1054. The Dominant Color (20)
- c++语法之冒号(:),双冒号(::)
- Tagview(LinearLayout 动态添加子Button,并自动换行 )