[10] 枚举
1、枚举类的定义和意义
1.1 基本概念
枚举类型(Enumerated Type) 用来将一组类似的值包含到一种类型当中。而这种枚举类型的名称则会被定义成独一无二的类型描述符。
枚举类型的出现有什么意义?为什么不可以用普通的常量直接定义来替代呢?我们先看一个示例,假如现在希望为彩虹描述出七种颜色,在Java中通过常量定义的方式来实现:
Public static class RainbowColor {
// 红橙黄绿青蓝紫七种颜色的常量定义
public static final int RED = 0;
public static final int ORANGE = 1;
public static final int YELLOW = 2;
public static final int GREEN = 3;
public static final int CYAN = 4;
public static final int BLUE = 5;
public static final int PURPLE = 6;
}
11
1
Public static class RainbowColor {
2
3
// 红橙黄绿青蓝紫七种颜色的常量定义
4
public static final int RED = 0;
5
public static final int ORANGE = 1;
6
public static final int YELLOW = 2;
7
public static final int GREEN = 3;
8
public static final int CYAN = 4;
9
public static final int BLUE = 5;
10
public static final int PURPLE = 6;
11
}
然后在程序中,你可以直接引用这些常量。那么问题来了:
- 类型不安全
- 由于颜色常量的对应值是整数形,所以程序执行过程中很有可能给颜色变量传入一个任意的整数值,导致出现错误。
- 没有命名空间
- 由于颜色常量只是类的属性,当你使用的时候不得不通过类来访问。
- 类型无指意性
- 由于颜色常量值仅仅是一些无任何含义的整数值,如果在运行期调试时候,你就会发现日志中有很多魔术数字,但除了程序员本身,其他人很难明白其奥秘。
1.2 如何定义一个Enum类
如上提到的彩虹色,你用枚举类可以这样做:
enum RainbowColor { RED, ORANGE, YELLOW, GREEN, CYAN, BLUE, PURPLE }
1
1
enum RainbowColor { RED, ORANGE, YELLOW, GREEN, CYAN, BLUE, PURPLE }
创建枚举类型要使用 enum 关键字,它本质上是一个类,其隐含的父类是java.lang.Enum(java.lang.Enum 是一个抽象类)。所以自动继承了Enum的一些方法。
枚举类型符合通用模式 Class Enum<E extends Enum<E>>,而 E 表示枚举类型的名称。
枚举类型的每一个值都将映射到 protected Enum(String name, int ordinal) 构造函数中,在这里,每个值的名称都被转换成一个字符串,并且序数设置表示了此设置被创建的顺序。
上面这些话的意思在于,假如有枚举类:
public enum Weekday {
MON, TUE, WED, THU, FRI, SAT, SUN;
}
3
1
public enum Weekday {
2
MON, TUE, WED, THU, FRI, SAT, SUN;
3
}
实际上这段代码调用了7次 Enum(String name, int ordinal):
new Enum<Weekday>("MON",0);
new Enum<Weekday>("TUE",1);
new Enum<Weekday>("WED",2);
...
4
1
new Enum<Weekday>("MON",0);
2
new Enum<Weekday>("TUE",1);
3
new Enum<Weekday>("WED",2);
4
...
取用的时候是这样:
Weekday wd = Weekday.MON;
1
1
Weekday wd = Weekday.MON;
枚举定义尽量以Enum结尾方便识别,如下的 WeekEnum
但枚举的基本使用,我们只能创建名称,例如星期,希望能有对应的值,怎么处理?枚举毕竟是一个类,所以完全可以这样:
// 定义一个星期的枚举类
public enum WeekEnum {
// 因为已经定义了带参数的构造器,所以在列出枚举值时必须传入对应的参数
SUNDAY("星期日"), MONDAY("星期一"), TUESDAY("星期二"), WEDNESDAY("星期三"),
THURSDAY("星期四"), FRIDAY("星期五"), SATURDAY("星期六");
// 定义一个 private 修饰的实例变量
private String value;
// 定义一个带参数的构造器,枚举类的构造器只能使用 private 修饰
private WeekEnum(String value) {
this.value = value;
}
// 定义 get set 方法
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
x
1
// 定义一个星期的枚举类
2
public enum WeekEnum {
3
4
// 因为已经定义了带参数的构造器,所以在列出枚举值时必须传入对应的参数
5
SUNDAY("星期日"), MONDAY("星期一"), TUESDAY("星期二"), WEDNESDAY("星期三"),
6
THURSDAY("星期四"), FRIDAY("星期五"), SATURDAY("星期六");
7
8
// 定义一个 private 修饰的实例变量
9
private String value;
10
11
// 定义一个带参数的构造器,枚举类的构造器只能使用 private 修饰
12
private WeekEnum(String value) {
13
this.value = value;
14
}
15
16
// 定义 get set 方法
17
public String getValue() {
18
return value;
19
}
20
21
public void setValue(String value) {
22
this.value = value;
23
}
24
25
}
1.3 Enum的好处
- 枚举类(对象)可以直接用于switch语句(实际上的原理是,Java编译器会自动在枚举成员上调用ordinal()方法)
- 作为类,它可以自定义属性和方法
- 有EnumMap和EnumSet两个工具类,同时也是Map和Set接口的实现类,详见 Java 语言中 Enum 类型的使用介绍
2、如何避免错误地使用Enum
(1)enum 类型不支持 public 和 protected 修饰符的构造方法,因此构造函数一定要是 private 的。也正因为如此,所以枚举对象是无法在程序中通过直接调用其构造方法来初始化的。
(2)定义 enum 类型时候,如果是简单类型,那么最后一个枚举值后不用跟任何一个符号;但如果有定制方法,那么最后一个枚举值与后面代码要用分号';'隔开,不能用逗号或空格。
(3)由于 enum 类型的值实际上是通过运行期构造出对象来表示的,因而在做比较操作时候就需要注意,如果直接通过使用等号 ( ‘ == ’ ) 操作符,这些看似一样的枚举值一定不相等,因为这不是同一个对象实例。
3、枚举的常用方法
- ordinal(),返回成员的声明顺序编号,初始为0,类似数组的索引
- name(),返回枚举成员的名称
- toString(),返回枚举常量的名称,和name()类似
- values(),常用来遍历,返回枚举类的数组(values()是由编译器添加的static方法)