Java 枚举

words: 1.4k    views:    time: 6min

枚举enum可以将一组具名的值得有限集合创建为一种新的类型,而这些具名的值可以作为常规的程序组件使用,这是一种非常有用的功能。

1. 枚举类实现

首先,enum是由class实现的,所以它可以实现很多class的内容,包括属性和方法,这也是enum可以作为一个类来使用的基础,其实它也是继承Enum实现的

java.lang.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
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
public abstract class Enum<E extends Enum<E>> implements Comparable<E>, Serializable {

private final String name;

public final String name() {
return name;
}

private final int ordinal;

public final int ordinal() {
return ordinal;
}

protected Enum(String name, int ordinal) {
this.name = name;
this.ordinal = ordinal;
}

public String toString() {
return name;
}

public final boolean equals(Object other) {
return this==other;
}

public final int hashCode() {
return super.hashCode();
}

protected final Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}

public final int compareTo(E o) {
Enum<?> other = (Enum<?>)o;
Enum<E> self = this;
if (self.getClass() != other.getClass() && self.getDeclaringClass() != other.getDeclaringClass())
throw new ClassCastException();
return self.ordinal - other.ordinal;
}

@SuppressWarnings("unchecked")
public final Class<E> getDeclaringClass() {
Class<?> clazz = getClass();
Class<?> zuper = clazz.getSuperclass();
return (zuper == Enum.class) ? (Class<E>)clazz : (Class<E>)zuper;
}

public static <T extends Enum<T>> T valueOf(Class<T> enumType, String name) {
T result = enumType.enumConstantDirectory().get(name);
if (result != null)
return result;
if (name == null)
throw new NullPointerException("Name is null");
throw new IllegalArgumentException(
"No enum constant " + enumType.getCanonicalName() + "." + name);
}

protected final void finalize() { }

private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
throw new InvalidObjectException("can't deserialize enum");
}

private void readObjectNoData() throws ObjectStreamException {
throw new InvalidObjectException("can't deserialize enum");
}
}

比如下面示例,通过javap -p Singleton.class或者反编译工具jad( http://www.javadecompilers.com/jad )看下其class

demo.Color
1
2
3
4
public enum Color {

RED, GREEN, BLUE;
}
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
public final class Color extends Enum{

private Color(String s, int i){
super(s, i);
}

public static Color[] values(){
Color acolor[];
int i;
Color acolor1[];
System.arraycopy(acolor = ENUM$VALUES, 0, acolor1 = new Color[i = acolor.length], 0, i);
return acolor1;
}

public static Color valueOf(String s){
return (Color)Enum.valueOf(demo/Color, s);
}

public static final Color RED;
public static final Color GREEN;
public static final Color BLUE;
private static final Color ENUM$VALUES[];

static {
RED = new Color("RED", 0);
GREEN = new Color("GREEN", 1);
BLUE = new Color("BLUE", 2);
ENUM$VALUES = (new Color[] {
RED, GREEN, BLUE
});
}
}

从class中可以看到,事实上枚举类也是用public static final列出了所有实例,与一般常量类的定义方式一样,只是相对于常量类,enum更为简洁,另外,这也是一种天然的线程安全的单例实现方式,并且可以防止序列化和反射。

2. 枚举类使用

静态导入:使用static import可以将enum实例的标识符带入当前的命名空间,所以使用时可以无需enum类型进行修饰

使用接口组织枚举:由于enum本身是继承实现的,所以不能再继承其它类,但是可以实现接口,这样当存在多个枚举类并存在一定关系时,可以使用接口来进行归类组织,比如列举食物下面的水果和蔬菜

1
2
3
4
5
6
7
8
9
10
public interface Food {

enum Fruit{
APPLE, PEACH, ORANGE;
}

enum Vegetables{
TOMATO, CARROT, POTATO;
}
}

然后就可以统一通过static import Food*进行引入

添加自定义方法:如果是共同的方法,则直接定义,与普通类一样,如果要为每个枚举实例赋予不同的行为,可以先定义一个abstract方法,然后再在实例中定义方法体。至于属性与构造器同样都可以自定义,与普通类无异。

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
public enum Color {

RED {
@Override
int calculate(int arg) {
return arg + this.ordinal();
}
},

GREEN {
@Override
int calculate(int arg) {
return arg - this.ordinal();
}
},

BLUE {
@Override
int calculate(int arg) {
return arg * this.ordinal();
}
};

String getName(){
return this.name();
}

abstract int calculate(int arg);
}

3. 枚举类的序列化

为了保证枚举类型像Java规范中所说的那样,每一个枚举类型极其定义的枚举变量在JVM中都是唯一的,在枚举类型的序列化和反序列化上,Java做了特殊的规定:

Enum constants are serialized differently than ordinary serializable or externalizable objects. The serialized form of an enum constant consists solely of its name; field values of the constant are not present in the form. To serialize an enum constant, ObjectOutputStream writes the value returned by the enum constant’s name method. To deserialize an enum constant, ObjectInputStream reads the constant name from the stream; the deserialized constant is then obtained by calling the java.lang.Enum.valueOf method, passing the constant’s enum type along with the received constant name as arguments. Like other serializable or externalizable objects, enum constants can function as the targets of back references appearing subsequently in the serialization stream. The process by which enum constants are serialized cannot be customized: any class-specific writeObject, readObject, readObjectNoData, writeReplace, and readResolve methods defined by enum types are ignored during serialization and deserialization. Similarly, any serialPersistentFields or serialVersionUID field declarations are also ignored–all enum types have a fixedserialVersionUID of 0L. Documenting serializable fields and data for enum types is unnecessary, since there is no variation in the type of data sent.

大意是,在序列化的时候Java仅仅是将枚举对象的name属性输出到结果中,反序列化的时候则是通过java.lang.Enum的valueOf()方法来根据名字查找枚举对象。并且,编译阶段就不允许任何对这种序列化机制的定制,因此禁用了writeObjectreadObjectreadObjectNoDatawriteReplacereadResolve等方法。

4. 枚举类的反射

ConstructornewInstance方法中,会判断是否为enum,如果是则抛出异常,因此枚举类不支持反射

java.lang.reflect.Constructor
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@CallerSensitive
public T newInstance(Object ... initargs) throws InstantiationException,
IllegalAccessException, IllegalArgumentException, InvocationTargetException {

if (!override) {
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
checkAccess(caller, clazz, null, modifiers);
}
}

if ((clazz.getModifiers() & Modifier.ENUM) != 0)
throw new IllegalArgumentException("Cannot reflectively create enum objects");

ConstructorAccessor ca = constructorAccessor; // read volatile
if (ca == null) {
ca = acquireConstructorAccessor();
}
@SuppressWarnings("unchecked")
T inst = (T) ca.newInstance(initargs);
return inst;
}


参考:

  1. 《java编程思想》