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() && // optimizationself.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。
