Java枚举类

定义

在某些情况下,一个类的对象是有限且固定的,比如季节类,行星类。这种实例有限且固定的类,被称为枚举类。

枚举类可以定义自己的成员变量,方法,实现一个或者多个接口,也可以定义自己的构造器。一个Java源文件最多只能定义一个public权限的枚举类,且文件名需要和这个枚举类相同。

和普通类的区别:

  • 可以实现一个或者多个接口,使用enum定义的枚举类默认继了java.lang.Enum类,而不是默认继承Object类,因此枚举类不能继承其他类型的类。其中java.lang.Enum实现了Serializable接口和Comparable接口。
  • 使用enum定义,非抽象的枚举类型,默认使用final修饰,因此枚举类不能派生子类。注意,即使是含有抽象方法的抽象枚举类也不能使用abstract修饰(因为系统会自动添加abstract修饰符)。
  • 枚举类的构造器只能使用private修饰符,如果省略了,则默认是,如果指定则只能指定private修饰符。
  • 枚举类的所有实例必须子枚举类的第一行全部列出,否则枚举类永远没法产生实例。列出这些实例的时候,系统会自动添加public static final修饰,无需程序员手动添加。

例子:

1
2
3
4
5
6
7
8
9
10
11
// JDK1.6 中switch加入了对枚举的支持
enum Signal {
GREEN, YELLOW, RED
}
....
switch (color) {
case RED:
color = Signal.GREEN;
break;
}
....

常用方法:

  • int compareTo(E o)

比较此枚举与指定对象的顺序。在该对象小于、等于或大于指定对象时,分别返回负整数、零或正整数。 枚举常量只能与相同枚举类型的其他枚举常量进行比较。

1
2
3
4
5
6
7
8
9
//  Enum 中的源码
public final int compareTo(E o) {
Enum other = (Enum)o;
Enum self = this;
if (self.getClass() != other.getClass() && // optimization
self.getDeclaringClass() != other.getDeclaringClass())
throw new ClassCastException();
return self.ordinal - other.ordinal;
}
  • String name()

返回此枚举实例的名称。

  • static values()

返回一个包含全部枚举值的数组,可以用来遍历所有枚举值。

  • String toString()

返回此枚举实例的名称,即枚举值。与 name() 一样。

1
2
3
4
5
6
7
//  Enum 中 name() 和 toString()
public String toString() {
return name;
}
public final String name() {
return name;
}
  • int ordinal()

返回枚举值在枚举类中的索引值(从0开始),即枚举值在枚举声明中的顺序,这个顺序根据枚举值声明的顺序而定。

  • ‘<’T extends Enum‘>’ valueOf()

返回带指定名称的指定枚举类型的枚举常量,名称必须与在此类型中声明枚举常量所用的标识符完全匹配(不允许使用额外的空白字符)。这个方法与toString相对应,因此重写 toString() 方法,一定要重写 valueOf()方法(我们可以重写 toString() 方法,但不能自己重写 valueOf() 方法,当我们重写 toString()方法时,valueOf() 方法会自动重写,不用我们理会。)

1
2
3
4
5
6
7
static enum  test{
A,B,C
}

System.out.println(Enum.valueOf(test.class,"A").ordinal());//0
System.out.println(Enum.valueOf(test.class,"B").ordinal());//1
System.out.println(Enum.valueOf(test.class,"C").ordinal());//2

枚举类的成员变量,方法和构造方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
public enum Color {  

RED("红色"), GREEN("绿色"), BLANK("白色"), YELLO("黄色");

// 成员变量
private String name;
// 构造方法
private Color(String name) {
this.name = name;
}

// get set 方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}

}

or

public enum Day {

MONDAY("星期一"),
TUESDAY("星期二"),
WEDNESDAY("星期三"),
THURSDAY("星期四"),
FRIDAY("星期五"),
SATURDAY("星期六"),
SUNDAY("星期日");//记住要用分号结束

private String desc;//中文描述

/**
* 私有构造,防止被外部调用
* @param desc
*/
private Day(String desc){
this.desc=desc;
}

/**
* 定义方法,返回描述,跟常规类的定义没区别
* @return
*/
public String getDesc(){
return desc;
}

public static void main(String[] args){
for (Day day:Day.values()) {
System.out.println("name:"+day.name()+
",desc:"+day.getDesc());
}
}

/**
输出结果:
name:MONDAY,desc:星期一
name:TUESDAY,desc:星期二
name:WEDNESDAY,desc:星期三
name:THURSDAY,desc:星期四
name:FRIDAY,desc:星期五
name:SATURDAY,desc:星期六
name:SUNDAY,desc:星期日
*/
}

上面的枚举类中都增加了带参数的构造器,所以在列出枚举值的时候,必须显示的传入参数。在列出枚举值的时候,实际上就是调用构造器创建枚举对象,只是这里无需使用new关键字,也无需显示的调用构造器。前面列出枚举值的时候无需传入参数,甚至无需使用括号,仅仅是因为前面的枚举类包含无参数的构造器。

枚举类实现接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
public interface Behaviour {  
void print();
}

public enum ColorA implements Behaviour{

RED("红色"), GREEN("绿色"), BLANK("白色"), YELLO("黄色");

// 成员变量
private String name;
// 构造方法
private ColorA(String name) {
this.name = name;
}

// get set 方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}

//接口方法
@Override
public void print() {
System.out.println(this.name);
}
}

or

public enum ColorB implements Behaviour{

RED("红色"){
@Override
public void print() {
System.out.println(RED.name);
}
}, GREEN("绿色"){
@Override
public void print() {
System.out.println(GREEN.name);
}
}, BLANK("白色"){
@Override
public void print() {
System.out.println(BLANK.name);
}
}, YELLO("黄色"){
@Override
public void print() {
System.out.println(YELLO.name);
}
};

// 成员变量
private String name;
// 构造方法
private ColorB(String name) {
this.name = name;
}

// get set 方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}

}

第二种实现中,四个枚举值都是ColorB的匿名内部子类。编译完成后会生成五个class文件,分别对应ColorB和四个枚举类值。

包含抽象方法的枚举类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public enum Operation {

// 用于执行加法运算
PLUS { // 花括号部分其实是一个匿名内部子类

@Override
public double calculate(double x, double y) {
return x + y;
}

},

// 用于执行减法运算
MINUS { // 花括号部分其实是一个匿名内部子类

@Override
public double calculate(double x, double y) {
// TODO Auto-generated method stub
return x - y;
}

},

// 用于执行乘法运算
TIMES { // 花括号部分其实是一个匿名内部子类

@Override
public double calculate(double x, double y) {
return x * y;
}

},

// 用于执行除法运算
DIVIDE { // 花括号部分其实是一个匿名内部子类

@Override
public double calculate(double x, double y) {
return x / y;
}

};

//为该枚举类定义一个抽象方法,枚举类中所有的枚举值都必须实现这个方法
public abstract double calculate(double x, double y);

}

编译上面的代码将生成五个class文件,Operation对应一个class文件,其他四个匿名内部子类分别对应一个class文件。
枚举类里面定义抽象方法的时候不能使用abstract将枚举类定义成抽象类(因为系统会自动为他添加abstract关键字),但是因为枚举类需要显示的创建枚举值,而不是作为父类,所以定义每个枚举值的时候必须为抽象方法提供实现,否则将出现编译错误。

建议阅读:

3分钟快速掌握枚举(enum)
小谈Java Enum的多态性
包含抽象方法的枚举类
深入理解Java枚举类型(enum)

当前网速较慢或者你使用的浏览器不支持博客特定功能,请尝试刷新或换用Chrome、Firefox等现代浏览器