values(), ordinal() 和 valueOf() 方法
枚举类中的抽象方法实现,需要枚举类中的每个对象都对其进行实现。
enum Color{
RED{
public String getColor(){//枚举对象实现抽象方法
return "红色";
}
},
GREEN{
public String getColor(){//枚举对象实现抽象方法
return "绿色";
}
},
BLUE{
public String getColor(){//枚举对象实现抽象方法
return "蓝色";
}
};
public abstract String getColor();//定义抽象方法
}
public class Test{
public static void main(String[] args) {
for (Color c:Color.values()){
System.out.print(c.getColor() + "、");
}
}
}
反编译分析
public enum Season {
SPRING, SUMMER, AUTUMN, WINTER;
}
用 javap 反编译一下生成的 class 文件:
public final class Season extends java.lang.Enum<Season> {
public static final Season SPRING;
public static final Season SUMMER;
public static final Season AUTUMN;
public static final Season WINTER;
public static Season[] values();
public static Season valueOf(java.lang.String);
static {};
}
可以看到,实际上在经过编译器编译后生成了一个 Season 类,该类继承自 Enum 类,且是 final 的。从这一点来看,Java 中的枚举类型似乎就是一个语法糖。
每一个枚举常量都对应类中的一个 public static final
的实例,这些实例的初始化应该是在 static {} 语句块中进行的。因为枚举常量都是 final 的,因而一旦创建之后就不能进行更改了。 此外,Season 类还实现了 values()
和 valueOf()
这两个静态方法。
再用 jad 进行反编译,我们可以大致看到 Season 类内部的实现细节:
public final class Season extends Enum
{
public static Season[] values()
{
return (Season[])$VALUES.clone();
}
public static Season valueOf(String s)
{
return (Season)Enum.valueOf(Season, s);
}
private Season(String s, int i)
{
super(s, i);
}
public static final Season SPRING;
public static final Season SUMMER;
public static final Season AUTUMN;
public static final Season WINTER;
private static final Season $VALUES[];
static
{
SPRING = new Season("SPRING", 0);
SUMMER = new Season("SUMMER", 1);
AUTUMN = new Season("AUTUMN", 2);
WINTER = new Season("WINTER", 3);
$VALUES = (new Season[] {
SPRING, SUMMER, AUTUMN, WINTER
});
}
}
枚举类的实现使用了一种多例模式,只有有限的对象可以创建,无法显示调用构造方法创建对象。
values()
方法返回枚举常量数组的一个浅拷贝,可以通过这个数组访问所有的枚举常量;而 valueOf()
则直接调用父类的静态方法 Enum.valueOf()
,根据传入的名称字符串获得对应的枚举对象。
Enum 类是不能被继承的,如果我们按照上面反编译的结果自己写一个这样的实现,是不能编译成功的。Java 编译器限制了我们显式的继承 java.Lang.Enum
类, 报错 The type may not subclass Enum explicitly
。
源码
//从类的声明来看,Enum 是个抽象类,且用到了泛型,
//类型参数的值必须要是 Enum 的子类。Enum 类还实现了 Comparable 和 Serializable 接口。
public abstract class Enum<E extends Enum<E>>
implements Comparable<E>, Serializable {
//Enum 类有两个私有的成员,name 和 ordinal,
//在 protected 的构造方法中初始化,
//分别是表示枚举常量名称的字符串、枚举常量在枚举定义中序号的整型变量。
//这两个常量当然也会被其子类继承,
//前面看到 Season 类的构造方法中就是直接调用父类的构造方法设置这两个成员的。
//name 和 ordinal 都是 final 修饰的,一旦初始化后就不能进行修改了。
private final String name;
public final String name() {
return name;
}
private final int ordinal; //从0开始
public final int ordinal() {
return ordinal;
}
protected Enum(String name, int ordinal) {
this.name = name;
this.ordinal = ordinal;
}
//直接使用 `==` 比较,不可在子类重写
public final boolean equals(Object other) {
return this==other;
}
// 返回该枚举常量的哈希码。和equals一致,该方法不可以被重写。
public final int hashCode() {
return super.hashCode();
}
//类型要相同,根据它们在枚举声明中的先后顺序来返回大小(前面的小,后面的大)。
//子类不可以重写该方法
public final int compareTo(E o) {
Enum<?> other = (Enum<?>)o;
Enum<E> self = this;
if (self.getClass() != other.getClass() && // optimization
self.getDeclaringClass() != other.getDeclaringClass())
throw new ClassCastException();
return self.ordinal - other.ordinal;
}
//valueOf() 方法根据传入的字符串返回对应名称的枚举常量。
//调用 Class 对象的 enumConstantDirectory() (package-private)
//方法会创建一个名称和枚举常量的 Map,然后以名称为键进行查找。
//返回带指定名称的指定枚举类型的枚举常量。
//名称必须与在此类型中声明枚举常量所用的标识符完全匹配。
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);
}
// 得到枚举常量所属枚举类型的Class对象
public final Class<E> getDeclaringClass() {
Class clazz = getClass();
Class zuper = clazz.getSuperclass();
return (zuper == Enum.class) ? clazz : zuper;
}
//枚举对象不能序列化和反序列化,也不允许克隆
protected final Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
/**
* enum classes cannot have finalize methods.
*/
protected final void finalize() { }
/**
* prevent default deserialization
*/
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");
}
EnumSet&EnumMap
EnumSet 是一个特殊的 Set,其内部的元素必须是来自同一个 enum。EnumSet 内部使用 bit 向量实现,这种实现方式更紧凑高效,类似于传统基于 int 的位标志。相比于位标志,EnumSet 的可读性更强,且性能上也相差不大。详细可参考官方 API。
EnumMap 是一种特殊的 Map,要求其中的键 (key) 必须来自于同一个 enum。由于 enum 自身的实例数量是有限的,EnumMap 在内部可由数组实现,因此速度很快。除了只能使用 enum 作为键以外,其它的操作和一般的 Map 没有太大区别。详细可参考官方 API。