关键字 enum 可以将一组具名的值的有限集合创建为一种新的类型。
1 基本 enum 特性
values() -> 返回枚举实例数组,并且是声明时的顺序;
ordinal() -> 返回一个 int 值,实例声明时的次序,从 0 开始;
Enum 类 实现了 Comparable 接口 和 Serializable 接口,具有 CompareTo() 方法;
name() toString() 返回实例声明时的名字
valueOf() 是在 Enum 中定义的 static 方法,根据给定的名字返回相应的 enum 实例;
将静态导入用于 enum import static com.paradise.think.enum19.Season.*;
static import 静态导入 有使用过,IDE有时会建议
2 向 enum 中添加新方法
除了不能继承自一个 enum 之外,我们基本上可以将 enum 看作一个常规的类。
只能在 enum 定义的内部使用其构造器创建 enum 实例。
一旦 enum 的定义结束,编译器就不允许我们再使用其构造器来创建任何实例了。
2.1 覆盖 enum 的方法
@Override
public String toString() {
return name().toLowerCase();
}
3 switch 语句中的 enum
一般来说,在 switch 中只能使用整数值,而枚举实例天生就具备整数值的次序。
虽然一般情况下我们必须使用 enum 类型来修饰一个 enum 实例,但是在 case 语句中却不必如此。
4 values() 的神秘之处
values() 是由编译器添加的 static 方法,同时还添加了 valueOf(String) 方法;
由于 values() 方法是由编译器插入到 enum 定义中的 static 方法,所以,如果你将 enum 实例向上转型为 Enum,那么 values() 方法就不可访问了。不过,在 Class 中有一个 getEnumConstants() 方法,可以通过 Class 对象取得 所有 enum 实例
5 实现,而非继承
所有的 enum 都继承自 java.lang.Enum 类;
Java 不支持多重继承;
可以实现一个或多个接口;
6 随机选取
package com.paradise.think.enum19;
import java.util.Random;
/**
* 随机选取
*
* @author Paradise
*/
public class Enums {
private static Random random = new Random(47);
public static <T extends Enum<T>> T random(Class<T> tc) {
return random(tc.getEnumConstants());
}
private static <T> T random(T[] values) {
return values[random.nextInt(values.length)];
}
}
enum Test {
/**
* 1
*/
One, Two, Three, Four, Five, Six;
}
class RandomTest {
public static void main(String[] args) {
System.out.println(Enums.random(Test.class));
}
}
虽然 Enum 只是一个相当短小的类,但是在本章你会发现,它能消除很多重复的代码。
重复总会制造麻烦,因此消除重复总是有益处的。
7 使用接口组织枚举
无法从 enum 继承子类有时很令人沮丧:这种需求有时源自
- 我们希望扩展原 enum 中的元素
- 我们希望使用子类将一个 enum 中的元素进行分组
在一个接口内部,创建实现该接口的枚举,以此将元素进行分组,可以达到将枚举元素分类组织的目的。
/**
* 使用接口组织枚举;达到分类的目的
*
* @author Paradise
*/
public interface Food {
enum MainFood implements Food {
/**
* 面包
*/
Bread, Rice, Sandwich, Hamburger;
}
enum Coffee implements Food {
/**
* 纯咖啡
*/
PureCoffee, Blank, WHITE;
}
}
对于 enum 而言,实现接口是使其子类化的唯一办法;
——
创建一个“枚举的枚举”:创建一个新的 enum,然后用其实例包装 类型中的 每一个 enum类;
enum Course {
/**
* 主食
*/
MainFood(Food.MainFood.class),
Coffee(Food.Coffee.class);
private Food[] values;
Course(Class<? extends Food> kind) {
values = kind.getEnumConstants();
}
public Food select() {
return Enums.random(values);
}
}
——
将一个 enum 嵌套在另一个 enum 内,仅仅是重新组织了一下代码,使你的代码具有更清晰的结构。
8 使用 EnumSet 替代标志
使用 EnumSet 的优点是:它在说明一个二进制位是否存在时,具有更好的表达能力,并且无需担心性能;
of() 方法被重载了很多次,接收2-5个显式的参数的情况都进行了重载,侧面反应了 EnumSet 对性能的关注;
EnumSet 的基础是 long,一个 long 值有 64位,而一个 enum 实例只需一位 bit 表示其是否存在。
if (universe.length <= 64)
return new RegularEnumSet<>(elementType, universe);
else
return new JumboEnumSet<>(elementType, universe);
9 使用 EnumMap
EnumMap 的速度很快;
命令设计模式;
与 EnumSet 一样, enum 实例定义的次序决定了其在 EnumMap 中的顺序
package com.paradise.think.enum19;
import java.util.EnumMap;
import java.util.Map;
interface Command {
/**
* action
*/
void action();
}
/**
* EnumMap
*
* @author Paradise
*/
public class EnumMaps {
public static void main(String[] args) {
EnumMap<AlarmPoints, Command> enumMap = new EnumMap<>(AlarmPoints.class);
enumMap.put(AlarmPoints.KITCHEN, () -> System.out.println(" Kitchen fire! "));
enumMap.put(AlarmPoints.LOBBY, () -> System.out.println(" lobby fire! "));
enumMap.put(AlarmPoints.OFFICE, () -> System.out.println(" Office fire! "));
for (Map.Entry<AlarmPoints, Command> entry : enumMap.entrySet()) {
System.out.println(entry.getKey());
entry.getValue().action();
}
try {
enumMap.get(AlarmPoints.STAIR1).action();
} catch (Exception e) {
System.out.println(e.toString());
}
}
}
10 常量相关的方法
与使用匿名内部类相比较,定义常量相关方法的语法更搞笑,简洁。
10.1 使用 enum 的职责链
在 职责链 (Chain of Responsibility)设计模式中,程序员以多种不同的方式来解决一个问题,然后将它们链接在一起。当一个请求到来时,它遍历这个链,直到链中的某个解决方案能够处理该请求。
代码地址:
职责链 由 enum MailHandler 实现,而 enum 定义的次序决定了各个解决策略在应用时的次序。