一个集的枚举是列出某些有穷序列集的所有成员的程序,或者是一种特定类型对象的计数。
enum
关键字定义。public
修饰,所以不能被实例化。为方便测试,我们定义这样的枚举类型:
public enum Color {
WHITE,
BLUE,
GREEN,
RED,
YELLOW,
BLACK //这里省略了分号,如果后续需定义其他成员,需加上分号
}
枚举一般应用于switch...case...default
语句中,配合内置的valueOf
方法提取字符串的值,具体使用案例如下:
public void whatColor(){
switch (Color.valueOf("YELLOW")){
case YELLOW:
System.out.println("黄色");
break;
default:
System.out.println("其他颜色");
break;
}
}
java.lang.IllegalArgumentException: No enum constant Color.XXX
我们打印输出,进行测试:
System.out.println(Color.valueOf("vovo"));
我们打印输出
System.out.println(Arrays.toString(Color.values()));
javap
指令,对 Color.java 文件进行反编译://Enum 类 不可被 我们随意 继承
public final class Color /*extends Enum*/ {
public static final Color WHITE;
public static final Color BLUE;
public static final Color GREEN;
public static final Color RED;
public static final Color YELLOW;
public static final Color BLACK;
static{
WHITE = new Color("WHITE");
BLUE = new Color("BLUE");
GREEN = new Color("GREEN");
RED = new Color("RED");
YELLOW = new Color("YELLOW");
BLACK = new Color("BLACK");
}
//定义变量color
private final String color;
//私有的构造器
private Color(String color) {
this.color = color;
}
//静态方法 values();
public static Color[] values() {
return new Color[] {WHITE,BLUE,GREEN,RED,YELLOW,BLACK};
}
//静态方法 valueOf(String color);
public static Color valueOf(String color) {
switch (color) {
case "WHITE":
return Color.WHITE;
case "BLUE":
return Color.BLUE;
case "GREEN":
return Color.GREEN;
case "RED":
return Color.RED;
case "YELLOW":
return Color.YELLOW;
case "BLACK":
return Color.BLACK;
default:
throw new IllegalArgumentException("No enum constant: " + Color.class.getName() + "." + color);
}
}
//为了防止其打印地址,我们只好重写toString方法,事实上enum类型并没有内置toString方法
@Override
public String toString() {
return this.color;
}
}
上述类同样可以完成枚举类的功能,只是这样写十分繁琐,使用枚举类型可以帮助我们简化代码,而且看起来十分简洁明了。
EnumMap是专门为枚举类型量身定做的Map实现,类似地还有EnumSet。虽然使用其它的Map实现(如HashMap)也能完成枚举类型实例到值得映射,但是使用EnumMap会更加高效:它只能接收同一枚举类型的实例作为键值,并且由于枚举类型实例的数量相对固定并且有限,所以EnumMap使用数组来存放与枚举类型对应的值。这使得EnumMap的效率非常高。
《Effective Java》中作者建议用EnumMap代替叙述索引,最好不要用序数来索引数组,而要使用EnumMap。
EnumMap构造方法采用键类型的Class对象:这是一个有限制的类型令牌,它提供了运行时的泛型信息。
其中Class
作为采用键类型的Class对象,说明EnumMap以枚举类型为键,可取出对应值。
为方便说明,我们定义如下的星期Enum类,并创建对应的EnumMap:
enum Weekday{
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY,
SUNDAY
}
初始化 键值:
这是 Java 中 Map、Set 初始化设定数值的途径,形式上类似于数组{}
,Map与Set使用双层花括号定义初始数值{{}}
。
//初始化直接放入键值
Map<Weekday,String> map = new EnumMap<>(Weekday.class){{
put(Weekday.MONDAY,"星期一");
put(Weekday.TUESDAY,"星期二");
put(Weekday.WEDNESDAY,"星期三");
put(Weekday.THURSDAY,"星期四");
put(Weekday.FRIDAY,"星期五");
put(Weekday.SATURDAY,"星期六");
put(Weekday.SUNDAY,"星期日");
}};
双层花括号的意义?
上面代码如果是写在 EnumMapDemo 类中,编译后你会看到会生成 EnumMapDemo$1.class 文件,编译 .java 文件后得到结果:
剖析这种方式,发现其本质上创建了一个继承 EnumMap 的匿名内部类(内部类概述):
// 第一个{}代表 创建了一个 HashMap 的子类
class EnumMapDemo$1<K extends Enum<K>, V> extends EnumMap {
{ // 第二个{}中的代码放到了实例初始化块中去了
put(Weekday.MONDAY, "星期一");
put(Weekday.TUESDAY, "星期二");
}
EnumMapDemo$1(Class<K> keyType){ //super调用父类构造方法
super(keyType);
}
}
初始化后操作键值:
//先创建枚举映射
Map<Weekday,String> map = new EnumMap<>(Weekday.class);
//后放入键值
map.put(Weekday.MONDAY,"星期一");
map.put(Weekday.TUESDAY,"星期二");
map.put(Weekday.WEDNESDAY,"星期三");
map.put(Weekday.THURSDAY,"星期四");
map.put(Weekday.FRIDAY,"星期五");
map.put(Weekday.SATURDAY,"星期六");
map.put(Weekday.SUNDAY,"星期日");
情景引入:
现有以 Java 语言编写的服务器、C# 语言 编写的客户端,二者为 C/S架构,均使用 Socket 进行通信。由于语言不相通,须手动构建请求、响应字符串,并指定状态码。
而数字难以识别,这时就可以在服务端使用 Enum 规定请求状态的英文、后以EnumMap
enum Status{
ACCEPT, //20
RESPONSE,//50
SERVER_CLOSING,//90
RESOURCES_NOT_FOUND,//60
ERROR, //40
SUCCESS //70
//...
}
Map<Status,Integer> statusMap = new EnumMap<>(Status.class){{
put(Status.ACCEPT,20);
put(Status.RESPONSE,50);
put(Status.SERVER_CLOSING,90);
put(Status.RESOURCES_NOT_FOUND,60);
put(Status.ERROR,40);
put(Status.SUCCESS,70);
//...
}};
也可以将值包装成类,内部附有状态码、方法字符串,利用反射自动寻找、处理业务。
EnumSet 是一个枚举类型元素集的高效实现。枚举类型只有有限个例,所以EnumSet 内部用位序列实现。如果对应的值在集中,则相应的位被置为1。
EnumSet类没用公共的构造器。可以使用静态工厂方法构造这个集:
enum Weekday{
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY,
SUNDAY
}
//全部值
EnumSet<Weekday> always = EnumSet.allOf(Weekday.class);
//无值
EnumSet<Weekday> never= EnumSet.noneOf(Weekday.class);
//范围值
EnumSet<Weekday> workday= EnumSet.range(Weekday.MONDAY,Weekday.FRIDAY);
//选中值
EnumSet<Weekday> mwf = EnumSet.of(Weekday.MONDAY,Weekday.SATURDAY,Weekday.FRIDAY)
⭐ E extends Enum 代表“E是一个枚举类型”。
⭐ 枚举类的特殊性正体现于此:所有的枚举类型都扩展于泛型 Enum 类(参考本文第一节)