Java 从 JDK 1.5 才有枚举,和泛型一样是后来补上的特性。晚加的语法要向后兼容,编译器会帮源码做很多事(类型擦除、桥接方法、foreach、装箱拆箱),术语叫语法糖。枚举也走这条路:表面是 enum,底下是 java.lang.Enum 子类。
1.5 增加了抽象类 java.lang.Enum 当所有枚举的基类,并提供 EnumSet / EnumMap。项目里更推荐用枚举常量,而不是接口常量或类常量。
一、枚举类型
日常开发常定义常量;《Effective Java》建议用枚举替换,可读性和约束都更好。enum 隐含继承 Enum(抽象类),符合 Class Enum<E> 模式。每个枚举值都会进 protected Enum(String name, int ordinal):名字变成字符串,ordinal 是声明顺序。
public enum Week {
MON, TUE, WED, THU, FRI, SAT, SUN;
}
上面实际相当于调了 7 次构造:MON 的 ordinal 是 0,TUE 是 1,以此类推。
| 用法 |
做什么 |
| 常量 |
把相关常量收进一个类型,还自带方法 |
| switch |
1.6 前 switch 只支持 int/char/enum,可读性更好 |
| 加方法 |
实例列表后加分号,且必须先写枚举实例 |
| 覆盖 / 接口 / 组织 |
可 toString、实现接口、用接口分组;不能再继承别的类 |
| 集合 |
EnumSet 不重复;EnumMap 的 key 必须是枚举 |
用法一:常量
1.5 之前只能 public static final。现在可以把一组常量收进枚举:
public enum Color {
RED, GREEN, BLANK, YELLOW
}
Color color = Color.RED; //color -> Red;
用法二:switch
enum Signal {
GREEN, YELLOW, RED
}
public class TrafficLight {
Signal color = Signal.RED;
public void change() {
switch (color) {
case RED:
color = Signal.GREEN;
break;
case YELLOW:
color = Signal.RED;
break;
case GREEN:
color = Signal.YELLOW;
break;
}
}
}
用法三:向枚举添加方法
自定义方法时,枚举实例列表最后要有分号,并且实例必须写在最前。
public enum Color {
RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4);
// 成员变量
private String name;
private int index;
// 构造方法
private Color(String name, int index) {
this.name = name;
this.index = index;
}
// 普通方法
public static String getName(int index) {
for (Color c : Color.values()) {
if (c.getIndex() == index) {
return c.name;
}
}
return null;
}
// get set 方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
}
用法四:覆盖方法
public class Test {
public enum Color {
RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4);
// 成员变量
private String name;
private int index;
// 构造方法
private Color(String name, int index) {
this.name = name;
this.index = index;
}
// 覆盖方法
@Override
public String toString() {
return this.index + "_" + this.name;
}
}
public static void main(String[] args) {
System.out.println(Color.RED.toString());
}
}
用法五:实现接口
枚举已经继承 Enum,Java 又不支持多继承,所以不能再 extends 别的类,但可以实现接口。
public interface Behaviour {
void print();
String getInfo();
}
public enum Color implements Behaviour {
RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4);
// 成员变量
private String name;
private int index;
// 构造方法
private Color(String name, int index) {
this.name = name;
this.index = index;
}
// 接口方法
@Override
public String getInfo() {
return this.name;
}
// 接口方法
@Override
public void print() {
System.out.println(this.index + ":" + this.name);
}
}
用法六:用接口组织枚举
public interface Food {
enum Coffee implements Food {
BLACK_COFFEE, DECAF_COFFEE, LATTE, CAPPUCCINO
}
enum Dessert implements Food {
FRUIT, CAKE, GELATO
}
}
用法七:EnumSet 与 EnumMap
/**
*
* 演示枚举类型的遍历
*/
private static void testTraversalEnum() {
Light[] allLight = Light.values();
for (Light aLight : allLight) {
System.out.println("当前灯name:" + aLight.name());
System.out.println("当前灯ordinal:" + aLight.ordinal());
System.out.println("当前灯:" + aLight);
}
}
/**
*
* 演示EnumMap的使用,EnumMap跟HashMap的使用差不多,只不过key要是枚举类型
*/
private static void testEnumMap() {
// 1.演示定义EnumMap对象,EnumMap对象的构造函数需要参数传入,默认是key的类的类型
EnumMap<Light, String> currEnumMap = new EnumMap<Light, String>(
Light.class);
currEnumMap.put(Light.RED, "红灯");
currEnumMap.put(Light.GREEN, "绿灯");
currEnumMap.put(Light.YELLOW, "黄灯");
// 2.遍历对象
for (Light aLight : Light.values()) {
System.out.println("[key=" + aLight.name() + ",value="
+ currEnumMap.get(aLight) + "]");
}
}
/**
*
* 演示EnumSet如何使用,EnumSet是一个抽象类,获取一个类型的枚举类型内容<BR/>
*
* 可以使用allOf方法
*/
private static void testEnumSet() {
EnumSet<Light> currEnumSet = EnumSet.allOf(Light.class);
for (Light aLightSetElement : currEnumSet) {
System.out.println("当前EnumSet中数据为:" + aLightSetElement);
}
}
}
更细的实现原理:java 枚举类型的实现原理。
一句话总结:枚举是 JDK 1.5 的语法糖,编译后仍是 Enum 子类;用它收拢常量、进 switch、加方法或实现接口,集合侧优先 EnumSet/EnumMap。