Java枚举类详解

一、Enum抽象类

     Java中定义的所有enum类型,实际上都继承了java.lang.Enum类。首先,将Enum类源码贴在这里:

package java.lang;

import java.io.Serializable;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.ObjectStreamException;

public abstract class Enum>
        implements Comparable, 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 self = this;
        if (self.getClass() != other.getClass() && // optimization
            self.getDeclaringClass() != other.getDeclaringClass())
            throw new ClassCastException();
        return self.ordinal - other.ordinal;
    }

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

    public static > T valueOf(Class 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");
    }
}

1、为什么类的声明这么奇怪,是Enum>,而不是简单的Enum?

    这种声明限制了,要显式继承Enum类的话,只能使用这种方式:class SubClass extends Enum,而不能是 :class SubClass extends Enum,否则就会因为违反约束条件而不能编译。

    声明成Enum>,则类型E定义的对象也可以使用Enum中的方法。不然,E就只是一个Object。

2、getDeclaringClass的方法体怎么这么奇怪?

    Enum是一个抽象类,不能声明实例。要把这个方法当做其实现类的方法来看待。

    仍然沿用上面的,假设有这样一个类:class SubClass extends Enum那么,clazz变量实际上是SubClass.class,zuper变量是Enum.class。因此,这个方法的返回值是SubClass.class。

    为什么这么奇怪呢,如果直接使用getClass(),返回的不也是SubClass.class吗?这说明,还是有zuper不等于Enum.class的情况出现的。详细的见下面的解析。

二、enum是个语法糖

    语法糖:新语法A的编译结果,和老语法B的编译结果一样,但A语法出现更晚,用起来也更简单,就可以认为A是一个语法糖。语法糖并没有为这种语言增加任何新的特性。

    enum实际上就是一种语法糖,它的编译到底等效于一种什么样的写法呢?

    例如有这样一个类:enum Gender { MALE, FEMALE }

    编译之后,等效于这个类。枚举值实际上就是这个类的public static final字段。

public final class Gender extends Enum {

  public static final Gender MALE;
  public static final Gender FEMALE;
  private static final Gender[] $VALUES;

  static {
    MALE = new Gender("MALE", 0);
    FEMALE = new Gender("FEMALE", 1);
    $VALUES = new Gender[] {Male, Female};
  }

  private Gender(String name, int original) {
    super(name, original);
  }

  public static Gender[] values() {
    return $VALUE.clone();
  }

  public static Gender valueOf(String name) {
    return Enum.valueOf(Gender.class, name);
  }
}

三、自行加了一些字段、方法的枚举   

再来看复杂一些。如果我们在Gender枚举中加上几个字段呢:

public enum Gender {
     MALE(1, 3), FEMALE(7, 8);

     private final int a;
     private final int b;

     Gender(int a, int b) {
          this.a = a;
         this.b = b;
     }

     public int getA() { return a; }
     public int getB() { return b; }
}
猜想会出现以下的变化:
public final class Gender extends Enum {
  private int a;
  private int b;

  public static final Gender MALE;
  public static final Gender FEMALE;
  private static final Gender[] $VALUES;

  static {
    MALE = new Gender("MALE", 0, 1, 3);
    FEMALE = new Gender("FEMALE", 1, 7, 8);
    $VALUES = new Gender[] {Male, Female};
  }

  private void $Gender(int a, int b) {
    this.a = a;
    this.b = b;
  }

  public int getA() { return a; }

  public int getB() { return b; }

  private Gender(String name, int original, int a, int b) {
    super(name, original);
    $Gender(a, b);
  }

  public static Gender[] values() {
    return $VALUE.clone();
  }

  public static Gender valueOf(String name) {
    return Enum.valueOf(Gender.class, name);
  }
}

四、带有方法的枚举

    让枚举带上一个方法:

enum GenderA {
     MALE {
         @Override
          public int getName() {
              return 3;
          }
     },
     FEMALE {
         @Override
          public int getName() {
               return 90;
          }
     };

     public abstract int getName();
}

    这下,编译出来的结果就不一样了。上面的枚举编译出来,只有1个class文件,而这个枚举编译出了3个class文件:GenderA.class  GenderA$1.class  GenderA$2.class

    此时,GenderA不再是一个普通类,而是一个abstract类(因为有没实现的getName方法),两个枚举值各是一个GenderA的派生类,对getName方法的实现不一样。

    由此可以返回到getDeclaringClass方法的问题。

public final Class getDeclaringClass() {
        Class clazz = getClass();
        Class zuper = clazz.getSuperclass();
        return (zuper == Enum.class) ? (Class)clazz : (Class)zuper;
} 

    如果是不带有abstract方法的枚举类,clazz就是枚举类,zuper就是Enum.class。

    如果是带有abstract方法的枚举类,clazz就是GenderA$1.class这种匿名类,而zuper才是枚举类。

你可能感兴趣的:(Java枚举类详解)