values(), ordinal() 和 valueOf() 方法

枚举类中的抽象方法实现,需要枚举类中的每个对象都对其进行实现。

  1. enum Color{
  2. RED{
  3. public String getColor(){//枚举对象实现抽象方法
  4. return "红色";
  5. }
  6. },
  7. GREEN{
  8. public String getColor(){//枚举对象实现抽象方法
  9. return "绿色";
  10. }
  11. },
  12. BLUE{
  13. public String getColor(){//枚举对象实现抽象方法
  14. return "蓝色";
  15. }
  16. };
  17. public abstract String getColor();//定义抽象方法
  18. }
  19. public class Test{
  20. public static void main(String[] args) {
  21. for (Color c:Color.values()){
  22. System.out.print(c.getColor() + "、");
  23. }
  24. }
  25. }

反编译分析

  1. public enum Season {
  2. SPRING, SUMMER, AUTUMN, WINTER;
  3. }

用 javap 反编译一下生成的 class 文件:

  1. public final class Season extends java.lang.Enum<Season> {
  2. public static final Season SPRING;
  3. public static final Season SUMMER;
  4. public static final Season AUTUMN;
  5. public static final Season WINTER;
  6. public static Season[] values();
  7. public static Season valueOf(java.lang.String);
  8. static {};
  9. }

可以看到,实际上在经过编译器编译后生成了一个 Season 类,该类继承自 Enum 类,且是 final 的。从这一点来看,Java 中的枚举类型似乎就是一个语法糖。

每一个枚举常量都对应类中的一个 public static final 的实例,这些实例的初始化应该是在 static {} 语句块中进行的。因为枚举常量都是 final 的,因而一旦创建之后就不能进行更改了。 此外,Season 类还实现了 values()valueOf() 这两个静态方法。

再用 jad 进行反编译,我们可以大致看到 Season 类内部的实现细节:

  1. public final class Season extends Enum
  2. {
  3. public static Season[] values()
  4. {
  5. return (Season[])$VALUES.clone();
  6. }
  7. public static Season valueOf(String s)
  8. {
  9. return (Season)Enum.valueOf(Season, s);
  10. }
  11. private Season(String s, int i)
  12. {
  13. super(s, i);
  14. }
  15. public static final Season SPRING;
  16. public static final Season SUMMER;
  17. public static final Season AUTUMN;
  18. public static final Season WINTER;
  19. private static final Season $VALUES[];
  20. static
  21. {
  22. SPRING = new Season("SPRING", 0);
  23. SUMMER = new Season("SUMMER", 1);
  24. AUTUMN = new Season("AUTUMN", 2);
  25. WINTER = new Season("WINTER", 3);
  26. $VALUES = (new Season[] {
  27. SPRING, SUMMER, AUTUMN, WINTER
  28. });
  29. }
  30. }

枚举类的实现使用了一种多例模式,只有有限的对象可以创建,无法显示调用构造方法创建对象。

values() 方法返回枚举常量数组的一个浅拷贝,可以通过这个数组访问所有的枚举常量;而 valueOf() 则直接调用父类的静态方法 Enum.valueOf(),根据传入的名称字符串获得对应的枚举对象。

Enum 类是不能被继承的,如果我们按照上面反编译的结果自己写一个这样的实现,是不能编译成功的。Java 编译器限制了我们显式的继承 java.Lang.Enum 类, 报错 The type may not subclass Enum explicitly

源码

  1. //从类的声明来看,Enum 是个抽象类,且用到了泛型,
  2. //类型参数的值必须要是 Enum 的子类。Enum 类还实现了 Comparable 和 Serializable 接口。
  3. public abstract class Enum<E extends Enum<E>>
  4. implements Comparable<E>, Serializable {
  5. //Enum 类有两个私有的成员,name 和 ordinal,
  6. //在 protected 的构造方法中初始化,
  7. //分别是表示枚举常量名称的字符串、枚举常量在枚举定义中序号的整型变量。
  8. //这两个常量当然也会被其子类继承,
  9. //前面看到 Season 类的构造方法中就是直接调用父类的构造方法设置这两个成员的。
  10. //name 和 ordinal 都是 final 修饰的,一旦初始化后就不能进行修改了。
  11. private final String name;
  12. public final String name() {
  13. return name;
  14. }
  15. private final int ordinal; //从0开始
  16. public final int ordinal() {
  17. return ordinal;
  18. }
  19. protected Enum(String name, int ordinal) {
  20. this.name = name;
  21. this.ordinal = ordinal;
  22. }
  23. //直接使用 `==` 比较,不可在子类重写
  24. public final boolean equals(Object other) {
  25. return this==other;
  26. }
  27. // 返回该枚举常量的哈希码。和equals一致,该方法不可以被重写。
  28. public final int hashCode() {
  29. return super.hashCode();
  30. }
  31. //类型要相同,根据它们在枚举声明中的先后顺序来返回大小(前面的小,后面的大)。
  32. //子类不可以重写该方法
  33. public final int compareTo(E o) {
  34. Enum<?> other = (Enum<?>)o;
  35. Enum<E> self = this;
  36. if (self.getClass() != other.getClass() && // optimization
  37. self.getDeclaringClass() != other.getDeclaringClass())
  38. throw new ClassCastException();
  39. return self.ordinal - other.ordinal;
  40. }
  41. //valueOf() 方法根据传入的字符串返回对应名称的枚举常量。
  42. //调用 Class 对象的 enumConstantDirectory() (package-private)
  43. //方法会创建一个名称和枚举常量的 Map,然后以名称为键进行查找。
  44. //返回带指定名称的指定枚举类型的枚举常量。
  45. //名称必须与在此类型中声明枚举常量所用的标识符完全匹配。
  46. public static <T extends Enum<T>> T valueOf(Class<T> enumType,
  47. String name) {
  48. T result = enumType.enumConstantDirectory().get(name);
  49. if (result != null)
  50. return result;
  51. if (name == null)
  52. throw new NullPointerException("Name is null");
  53. throw new IllegalArgumentException(
  54. "No enum constant " + enumType.getCanonicalName() + "." + name);
  55. }
  56. // 得到枚举常量所属枚举类型的Class对象
  57. public final Class<E> getDeclaringClass() {
  58. Class clazz = getClass();
  59. Class zuper = clazz.getSuperclass();
  60. return (zuper == Enum.class) ? clazz : zuper;
  61. }
  62. //枚举对象不能序列化和反序列化,也不允许克隆
  63. protected final Object clone() throws CloneNotSupportedException {
  64. throw new CloneNotSupportedException();
  65. }
  66. /**
  67. * enum classes cannot have finalize methods.
  68. */
  69. protected final void finalize() { }
  70. /**
  71. * prevent default deserialization
  72. */
  73. private void readObject(ObjectInputStream in) throws IOException,
  74. ClassNotFoundException {
  75. throw new InvalidObjectException("can't deserialize enum");
  76. }
  77. private void readObjectNoData() throws ObjectStreamException {
  78. throw new InvalidObjectException("can't deserialize enum");
  79. }

EnumSet&EnumMap

EnumSet 是一个特殊的 Set,其内部的元素必须是来自同一个 enum。EnumSet 内部使用 bit 向量实现,这种实现方式更紧凑高效,类似于传统基于 int 的位标志。相比于位标志,EnumSet 的可读性更强,且性能上也相差不大。详细可参考官方 API
EnumMap 是一种特殊的 Map,要求其中的键 (key) 必须来自于同一个 enum。由于 enum 自身的实例数量是有限的,EnumMap 在内部可由数组实现,因此速度很快。除了只能使用 enum 作为键以外,其它的操作和一般的 Map 没有太大区别。详细可参考官方 API