Java中的枚举类型enum

枚举(enum)类型是Java 5新增的特性,它是一种新的类型,允许用常量来表示特定的数据片断,而且全部都以类型安全的形式来表示。
在JDK1.5之前,我们定义常量都是:public static fianl….。现在好了,有了枚举,可以把相关的常量分组到一个枚举类型里,而且枚举提供了比常量更多的方法。

虽然在开发过程中不经常使用,但还是要了解一下最基本的应用

一、enum

1、定义

下面以《Effective Java》书中的一个例子来说明,比如一个枚举类型,来表示计算器的四个基本运算:

1
2
3
public enum Operation {
PLUS, MINUS, TIMES, DIVIVE;
}

创建enum时,编译器会自动为我们生成一个继承自Java.lang.Enum的类,我们上面的enum可以简单看作下面的过程。当然在Java中是不能这样定义的,但是可以这样理解

1
2
3
4
5
public class Operation extends Enum{
public static final Operation PLUS;
public static final Operation MINUS;
...
}

对于上面的例子,我们可以把Operation看作一个类,而把PLUS, MINUS, TIMES, DIVIVE看作类的Operation的实例。
当然,这个构建实例的过程不是我们做的,一个enum的构造方法限制是private的,也就是不允许我们调用。

简单理解就是:我们创建了一种特殊的类,这个类的实例只有固定的几个,而且这些实例通过static final 声明为这个类的成员。

枚举类型也可以像正常类一样添加成员变量和方法

2、属性

比如操作的属性有一个符号属性,正如书中提到

枚举类型天生就是不可变的,因此所有的域都应该为final的。它们可以是共有的,单最好将它们做成是私有的,并提供共有的访问方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public enum Operation {
PLUS("+"),
MINUS("-"),
TIMES("*"),
DIVIVE("/");

private final String symbol;

Operation(String symbol) {
this.symbol = symbol;
}

@Override
public String toString() {
return symbol;
}
}

一般都是将属性声明为private final,这样就强制在新建枚举常量的时候初始化了。

3、方法

有一种方法可以将不同的行为与每个枚举常量关联起来:在枚举类型中声明一个抽象的方法,并且在特定于常量的类主体中,用每个具体的方法覆盖这个常量的抽象方法。

比如Operation类要有一个apply方法,用来计算,那么每操作对应的apply方法自然的不同的,就可以定义一个抽象方法:

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
public enum Operation {
PLUS("+") {
double apply(double x, double y) { return x + y; }
},
MINUS("-") {
double apply(double x, double y) { return x - y; }
},
TIMES("*") {
double apply(double x, double y) { return x * y; }
},
DIVIVE("/") {
double apply(double x, double y) { return x / y; }
};

private final String symbol;

Operation(String symbol) {
this.symbol = symbol;
}

@Override
public String toString() {
return symbol;
}

abstract double apply(double x, double y);
}

这样做的好处就是,如果后面给Operation添加新的常量,就不可能忘记覆盖apply方法。

可以看出,通过成员变量声明为final和方法声明为abstract,保证了每个枚举常量的属性和方法都必须在最开始新建的时候就确定,并且不可改变。

4、使用

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
package hello.enumDemo;

public enum Operation {
PLUS("+") {
double apply(double x, double y) { return x + y; }
},
MINUS("-") {
double apply(double x, double y) { return x - y; }
},
TIMES("*") {
double apply(double x, double y) { return x * y; }
},
DIVIDE("/") {
double apply(double x, double y) { return x / y; }
};

private final String symbol;

Operation(String symbol) {
this.symbol = symbol;
}

@Override
public String toString() {
return symbol;
}

abstract double apply(double x, double y);

private static boolean isPLUS(Operation op) {
return op.equals(Operation.PLUS);
}

private static void showOperation(Operation op) {
switch (op) {
case PLUS:
System.out.println("I am PLUS");
break;
case MINUS:
System.out.println("I am MINUS");
break;
case TIMES:
System.out.println("I am TIMES");
break;
case DIVIDE:
System.out.println("I am DIVIDE");
break;
default:
break;
}
}

public static void main(String[] args) {
double x = 0.1234;
double y = 3.6789;
for (Operation op : Operation.values()) {
System.out.printf("%f %s %f = %f%n", x, op, y, op.apply(x, y));
}

System.out.println(Operation.PLUS);//+
System.out.println(isPLUS(Operation.PLUS));//true
System.out.println(isPLUS(Operation.MINUS));//false
showOperation(Operation.DIVIDE);//I am DIVIDE
}
}

输出内容:

1
2
3
4
5
6
7
8
0.123400 + 3.678900 = 3.802300
0.123400 - 3.678900 = -3.555500
0.123400 * 3.678900 = 0.453976
0.123400 / 3.678900 = 0.033543
+
true
false
I am DIVIDE

可以看出枚举类型不光可以有实例方法,也可以有静态类方法。

还有就是values()方法,这是枚举类型自带的一个静态方法,按照声明顺序返回它的值数组。

二、enum实现单例模式

目前最为安全的实现单例的方法是通过内部静态enum的方法来实现,因为JVM会保证enum不能被反射并且构造器方法只执行一次。

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
public class EnumSingleton {
public static EnumSingleton getInstance(){
return Singleton.INSTANCE.getInstance();
}

private static enum Singleton{
INSTANCE;

private EnumSingleton singleton;

private Singleton(){
singleton=new EnumSingleton();
}

public EnumSingleton getInstance(){
return singleton;
}
}

public static void main(String[] args) {
EnumSingleton a=EnumSingleton.getInstance();
EnumSingleton b=EnumSingleton.getInstance();
System.out.println(a==b);//true
}
}

再来看一下enum的定义;

1
2
public abstract class Enum<E extends Enum<E>>
implements Comparable<E>, Serializable

可以看到,枚举也提供了序列化机制。某些情况,比如我们要通过网络传输一个数据库连接的句柄,会提供很多帮助。
书中推荐这个方法:单元素的枚举类型已经成为实现Singleton的最佳方法。