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 0
BANANA.ordinal 1
PEAR.ordinal 2
ORANGE.ordinal 3
TOMATO.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 时,编译器会生成一个 相关的类,上面例子中,就生成了一个 EnumKeyword$Fruits.class 这个 类是继承于 java.lang.Enum 的,所以 图1.1的 属性和方法, 生成的 Fruits.class 都有. 下图为 eclipse 编译生成的 class 文件
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() 用 == 比较的 ||||false
class com.crg.enumDemo.EnumKeyword$Fruits enum声明的类型
====================================================
BANANA fruit.ordinal 1 enum 实例的序列号,从0开始
BANANA fruit.name BANANA enum 的实例名字 和 toString() 返回的一样
-1 compareTo() 返回序列号的差 ||||false equals() 用 == 比较的 ||||false
class com.crg.enumDemo.EnumKeyword$Fruits enum声明的类型
====================================================
PEAR fruit.ordinal 2 enum 实例的序列号,从0开始
PEAR fruit.name PEAR enum 的实例名字 和 toString() 返回的一样
0 compareTo() 返回序列号的差 ||||true equals() 用 == 比较的 ||||true
class com.crg.enumDemo.EnumKeyword$Fruits enum声明的类型
====================================================
ORANGE fruit.ordinal 3 enum 实例的序列号,从0开始
ORANGE fruit.name ORANGE enum 的实例名字 和 toString() 返回的一样
1 compareTo() 返回序列号的差 ||||false equals() 用 == 比较的 ||||false
class com.crg.enumDemo.EnumKeyword$Fruits enum声明的类型
====================================================
TOMATO fruit.ordinal 4 enum 实例的序列号,从0开始
TOMATO fruit.name TOMATO enum 的实例名字 和 toString() 返回的一样
2 compareTo() 返回序列号的差 ||||false equals() 用 == 比较的 ||||false
class com.crg.enumDemo.EnumKeyword$Fruits enum声明的类型
====================================================
====================================================
====================================================
APPLE
BANANA
PEAR
ORANGE
TOMATO
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));
}
}
APPLE
ORANGE
PEAR
BANANA
TOMATO
使用 import static 能够将 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定义结束,编译器就不允许我们再使用构造器创建任何手机实例.
上面的例子已经看到, 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 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 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 enumMethods = analyze(Enum.class);
// 获得 Fruits.class 的所有方法
Set 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.Enum
Methods:
[compareTo, equals, getClass, getDeclaringClass, hashCode, name, notify, notifyAll, ordinal, toString, valueOf, values, wait]
----------------------------------------------------
fruitMethods.containsAll(enumMethods) ? : true
fruitMethods.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);
}
APPLE
BANANA
PEAR
ORANGE
TOMATO
getEnumConstants() 只针对枚举类,如果非枚举类,调用此方法,将会发生异常
Class 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{
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 next();
}
public static void PrintNext(Generator 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{
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 randomEnum(Class ls){
return random(ls.getEnumConstants());
}
/**
* 随机产生一个 values 的数组元素
* @param values
* @return
*/
public static 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 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_NOODLE
FRIED_RICE
BROCCOLI
=================================
POCKET_NOODLE
FRIED_RICE
POTATO
=================================
POUPO_NOODLE
GAIJIAO_RICE
BROCCOLI
=================================
POUPO_NOODLE
GAIJIAO_RICE
TOMATO
=================================
POCKET_NOODLE
FRIED_RICE
BROCCOLI
=================================
上面这个例子就是获取”枚举的枚举”的例子.
还可以重新组织代码,使代码有更清晰的结构,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 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("=================================");
}
}
}
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 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 是一种 特殊的 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 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 e : en.entrySet()) {
System.out.println(e.getKey());
e.getValue().action();
}
// EnumMap 里没有这个key
en.get(AlarmPoints.classroom4).action();
}
}
classroom1
this is classroom1
classroom2
this is classroom2
classroom3
this is classroom3
Exception 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, 2016
null
1.8.0_92
在这个例子中,每个enum实例,在 调用 getInfo() 具有不同的行为,体现出了多态的行为.enum实例与类的相似之处仅限于此了.并不能把enum实例当做类来使用.
再看一个例子:
package com.crg.constant;
import java.util.EnumSet;
public class TestConstant {
EnumSet 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, 2016
null
1.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 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, 2016
say hello
DATE_TIME say hello
null
say hello
1.8.0_92
say hello
在职责链(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 randomEnum(Class ls){
return random(ls.getEnumConstants());
}
/**
* 随机产生一个 values 的数组元素
* @param values
* @return
*/
public static T random(T[] values){
Random random = new Random();
return values[random.nextInt(values.length)];
}
public static Iterable generator(final int count){
return new Iterable() {
int n = count;
@Override
public Iterator iterator() {
// TODO Auto-generated method stub
return new Iterator() {
@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: OK4
using general delivery for Mail 0
delivering Mail 0 automatically
delivering Mail 0 normally
returning Mail 0 to sender
==================================================
Mail 1 |GeneralDelivery: NO2 |Scannability: YES3 |Readability: YES3 |Address: OK3 |ReturnAddress: MISSING
Mail 1 is a dead letter
==================================================
Mail 2 |GeneralDelivery: NO5 |Scannability: YES4 |Readability: ILLEGIBLE |Address: INCORRECT |ReturnAddress: OK3
Mail 2 is a dead letter
==================================================
Mail 3 |GeneralDelivery: NO2 |Scannability: YES1 |Readability: YES2 |Address: INCORRECT |ReturnAddress: OK5
Mail 3 is a dead letter
==================================================
Mail 4 |GeneralDelivery: NO2 |Scannability: YES2 |Readability: YES1 |Address: OK1 |ReturnAddress: OK3
Mail 4 is a dead letter
==================================================
Mail 5 |GeneralDelivery: NO2 |Scannability: YES1 |Readability: YES2 |Address: OK2 |ReturnAddress: OK2
Mail 5 is a dead letter
==================================================
Mail 6 |GeneralDelivery: NO4 |Scannability: YES1 |Readability: YES2 |Address: OK1 |ReturnAddress: OK1
Mail 6 is a dead letter
==================================================
Mail 7 |GeneralDelivery: NO2 |Scannability: YES4 |Readability: YES1 |Address: OK1 |ReturnAddress: OK1
Mail 7 is a dead letter
==================================================
Mail 8 |GeneralDelivery: NO5 |Scannability: YES3 |Readability: YES1 |Address: OK3 |ReturnAddress: OK3
Mail 8 is a dead letter
==================================================
Mail 9 |GeneralDelivery: NO3 |Scannability: YES4 |Readability: ILLEGIBLE |Address: OK4 |ReturnAddress: OK5
Mail 9 is a dead letter
==================================================
责任链由 enum MailHandler 实现,enum 的定义次序决定了各个解决策略在应用时的次序.对每一封邮件都按该次序尝试每一个解决策略,如果有一个策略,失败,该邮件被判定为死件.
==============================================================================
上面的代码下载地址,请点击打开: