做業16:java枚舉類的祕密

  • JAVA代碼
public enum EnumTest {
    HELLO,WORLD
}
  • 字節碼
public final class EnumTest extends java.lang.Enum<EnumTest> {
  public static final EnumTest HELLO;

  public static final EnumTest WORLD;

  public static EnumTest[] values();
    Code:
       0: getstatic     #1                  // Field $VALUES:[LEnumTest;
       3: invokevirtual #2                  // Method "[LEnumTest;".clone:()Ljava/lang/Object;
       6: checkcast     #3                  // class "[LEnumTest;"
       9: areturn                           // 從當前方法返回對象引用

  // 調用了(EnumTest)java.lang.Enum.valueOf(EnumTest.class,String)
  public static EnumTest valueOf(java.lang.String);
    Code:
       0: ldc           #4                  // class EnumTest
       2: aload_0
       3: invokestatic  #5                  // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
       6: checkcast     #4                  // class EnumTest
       9: areturn

  static {};
    Code:
       0: new           #4                  // class EnumTest
       3: dup
       4: ldc           #7                  // String HELLO
       6: iconst_0
       7: invokespecial #8                  // Method "<init>":(Ljava/lang/String;I)V
      10: putstatic     #9                  // Field HELLO:LEnumTest;
      13: new           #4                  // class EnumTest
      16: dup
      17: ldc           #10                 // String WORLD
      19: iconst_1
      20: invokespecial #8                  // Method "<init>":(Ljava/lang/String;I)V
      23: putstatic     #11                 // Field WORLD:LEnumTest;
      26: iconst_2
      27: anewarray     #4                  // class EnumTest, 建立一個引用型(如類,接口,數組)的數組,並將其引用值壓入棧頂
      30: dup
      31: iconst_0
      32: getstatic     #9                  // Field HELLO:LEnumTest;獲取指定類的靜態域,並將其值壓入棧頂
      35: aastore
      36: dup
      37: iconst_1
      38: getstatic     #11                 // Field WORLD:LEnumTest;
      41: aastore
      42: putstatic     #1                  // Field $VALUES:[LEnumTest;用棧頂的值爲指定的類的靜態域賦值
      45: return
}
  • 參考源碼實現的枚舉(沒有繼承 java.lang.Enum):
public final class MyEnum {
    private final String s;

    private MyEnum(String s) {
        this.s = s;
    }

    public static final MyEnum HELLO = new MyEnum("HELLO");
    public static final MyEnum WORLD = new MyEnum("WORLD");

    private static volatile MyEnum[] $VALUES;

    static {
        Field[] fields = MyEnum.class.getFields();
        List<Field> list = Arrays.asList(fields);
        $VALUES = list.stream().map(field -> {
            try {
                return field.get(MyEnum.class);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
                return null;
            }
        }).collect(Collectors.toList()).toArray(new MyEnum[fields.length]);
    }

    public static MyEnum[] values() {
        return $VALUES;
    }

    public static MyEnum valueOf(String name) throws NoSuchFieldException, IllegalAccessException {
        Class<MyEnum> clazz = MyEnum.class;
        Field field = clazz.getField(name);
        return (MyEnum) field.get(clazz);
    }

    @Override
    public String toString() {
        return s;
    }
}
  • java.lang.Enum類
// Enum 不能直接被繼承
// 下列省略部分代碼
public abstract class Enum<E extends Enum<E>>
        implements Comparable<E>, Serializable {
   
    private final String name;
    
    private final int ordinal;

    public final String name() {
        return name;
    }

    public final int ordinal() {
        return ordinal;
    }

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

    public String toString() {
        return name;
    }
    
    // 比較ordinal值,用於排序
    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;
    }

    @SuppressWarnings("unchecked")
    public final Class<E> getDeclaringClass() {
        Class<?> clazz = getClass(); // 獲取當前類的Class
        Class<?> zuper = clazz.getSuperclass(); // 獲取當前類的父類
        // 若是父類是Enum.class,則返回當前類的Class,不然返回父類的Class
        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;
        // 爲何不先判斷name是否等於null?
        if (name == null) throw new NullPointerException("Name is null");
        throw new IllegalArgumentException("No enum constant " + enumType.getCanonicalName() + "." + name);
    }

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

public final class Class<T>{
     
     private transient volatile Map<String, T> enumConstantDirectory;
     
     // 簡單的說生成枚舉Map:{"HEELO":EnumTest.HELLO,"WORLD":EnumTest.WORLD}  
     Map<String, T> enumConstantDirectory() {
        Map<String, T> directory = enumConstantDirectory;
        if (directory == null) {
            T[] universe = getEnumConstantsShared();
            if (universe == null)
                throw new IllegalArgumentException(getName() + " is not an enum type");
            directory = new HashMap<>(2 * universe.length);
            for (T constant : universe) {
                directory.put(((Enum<?>)constant).name(), constant);
            }
            enumConstantDirectory = directory;
        }
        return directory;
    }
    
    private transient volatile T[] enumConstants;
    
    T[] getEnumConstantsShared() {
        T[] constants = enumConstants;
        if (constants == null) {
            // 非Enum 直接返回null
            if (!isEnum()) return null;
            try {
                final Method values = getMethod("values");
                // 提高訪問修飾
                java.security.AccessController.doPrivileged(
                    new java.security.PrivilegedAction<>() {
                        public Void run() {
                                values.setAccessible(true);
                                return null;
                            }
                        });
                // 反射調用values方法,獲得枚舉數組
                @SuppressWarnings("unchecked")
                T[] temporaryConstants = (T[])values.invoke(null);
                enumConstants = constants = temporaryConstants;
            }catch (InvocationTargetException | NoSuchMethodException |IllegalAccessException ex) { 
                return null; 
            }
        }
        return constants;
    }
    
    public boolean isEnum() {
        // 返回此類或接口以整數編碼的 Java語言修飾符,如:public private protected、enum等。
        // 若是modifers有enum修飾語ENUM &操做將獲得ENUM,不等於0。
        return (this.getModifiers() & ENUM) != 0 &&
        this.getSuperclass() == java.lang.Enum.class;
    }
}
  • 測試
Class<?> aClass = EnumTest.HELLO.getClass();
System.out.println(aClass); // class EnumTest
System.out.println(aClass.getSuperclass()); // class java.lang.Enum
  • 結論
    • 枚舉類是一個普通的java類,enum類型只是java的語法糖,編譯器幫助開發人員轉化爲Eunm類。
    • 枚舉類繼承了java.lang.Enum類,valueOf和values方法繼承自Enum類。
    • java.lang.Enum類不能被直接繼承,因此本身實現的valueOf的實現採用了反射獲取字段。
    • java.lang.Enum類的valueOf實現採用了Class類反射和內部的map緩存。
相關文章
相關標籤/搜索