java原始碼學習之Enum

親吻昨日的陽光發表於2017-03-21

Enum的API規範出處

JSR 161

Enum的優點

  1. 編譯時的型別安全;
  2. 效能與int常量可比;
  3. 型別系統為每個Enum型別提供了一個名稱空間,這樣不必每個常量設定字首;
  4. 型別安全的常量不會編譯進客戶端,因此可以在不重新編譯的前提下新增、重排序、甚至刪除常量,因為匯出常量的屬性在列舉型別和它的客戶端之前提供了一個隔離層:常量值並沒有被編譯到客戶端程式碼中,而是在int列舉模式中;
  5. 通過toString可以將列舉轉換為可列印的字串,列印值資訊(在堆疊中可以看到name或value);
  6. Enum可以被用於集合中(如作為HashMap的key);
  7. 可以為一個Enum類新增任意的屬性和方法;
  8. Enum型別可以實現任意的介面。

另外:

  • Enum型別可以用於switch結構;
EType eType=EType.ONE;
switch (eType){
      case ONE:
            break;
      case TWO:
            break;
      case THREE:
            break;
      default:
}
  • Enum型別簡單可讀。

一個Enum型別對於每個被命名的enum常量都有public並且型別安全的成員。所有的Enum類都有高效的toStringhashCodeequals等方法,但沒有Cloneable方法。實現了SerializableComparable介面。除了toString外的所有方法都是final型別的。因為比較關係序列化和比較,所以要保證他們是正確的。

自定義的屬性可以新增到enum類,也可以新增到單個常量中。雖然通常在有經驗的程式設計師中使用,但大大提高了該特性的功能。

屬性是用來識別class檔案中的enum類和enum常量。

Enum是所有列舉類的父類,並且提議實現了專用用途的Set和Map,位於java.util包中,稱為EnumSetEnumMap,其所有的例項或者key是簡單的列舉類的例項。

列舉類完美相容標準集合的實現,特殊用途的實現僅僅為了提高效能。

所有的列舉類宣告預設都是final型別的,除非它們包含常量指定的類主體,列舉成員類是隱式的static。

不管使用什麼修飾符,顯式例項化列舉類都是非法的。

構造方法不能顯式呼叫父類構造方法,會報編譯器錯誤。如下:

enum EType{
      ONE,TWO,THREE;
      EType(){
            //該呼叫方式不允許,是由編譯器自動處理的。
            super();
      }

}

不過同一列舉類中的一個構造方法可以呼叫另一個,而且所有的構造方法潛在都是private修飾的,而且只能被private修飾。

enum EType{
      ONE,TWO,THREE;
      EType(){
            //這樣的呼叫是被允許的
            this(3);
      }
      EType(int i){}
}

引數

每個列舉常量可以有引數,該引數是在常量建立的時候通過構造方法設定的,而且構造方法的預設和建立正常的類是相似的,未宣告構造方法會生成預設無參構造方法,宣告瞭構造方法就會用已經宣告的方法。

列舉宣告外的方法

列舉宣告不包含那些與自動生成的成員衝突的方法:包括(VALUESfamily()readObject(ObjectInputStream)writeObject(ObjectOutputStream)),相似的,列舉宣告也不包含那些在java.util.Enum中定義為final的方法,包括(equals(Object)hashCode()clone()compareTo(Object)readResolve()),同時也不包含ordinalname

序列化

列舉的序列化形式是包含常量的名字,如果反序列化時,序列化名字中沒有正確型別的常量,則反序列化會報InvalidObjectException異常。列舉常量的反序列化將不會被重新排序,新增列舉常量,或刪除未使用的列舉常量。

語義

列舉宣告宣告的列舉類和普通類在可訪問的修飾符方面有相同的可見性。任何列舉中的方法宣告的可見性也和普通類的相同.特定於常量的類主體定義在enum中定義內部類來擴充套件封閉的enum類。所以該類中的例項方法如果覆蓋了修飾符為可訪問則可以被外界訪問。但是靜態方法和屬性不能被指定常量所在類之外的類主體訪問。

除了繼承自Enum類的成員,列舉類還有一個為每個宣告的列舉常量宣告“自我型別”的public static final屬性。

Enum不能被new例項化,也不能被克隆並且完全控制序列化和反序列化過程,這確保了在以上欄位之外沒有例項可用。因為每個值就是一個例項,所以在使用equal的地方可以使用==操作符來確定兩個物件是引用了同一個列舉常量。

列舉類自動生成如下屬性:

public static List<this enum class> VALUES;
public final List<this enum class> family();
public static <this enum class> valueOf(String name);

注意事項

  • 如果一個列舉具有普遍適用性,它就應該成為頂層類;如果只是被用於某個頂層類中,那麼它就應該成為該頂層類的成員類。

  • 如果在列舉型別中覆蓋toString方法,要考慮編寫fromString方法,將定製的字串表示法變回相應的列舉。

  • 如果多個列舉常量同時共享相同的香味,則考慮策略列舉。

  • 永遠不要根據列舉的序數匯出與它關聯的值,而是要將其存在例項域中。

  • 特定於常量的方法實現:即在列舉中宣告抽象方法,然後在特定於常量的類主體中用具體的方法覆蓋每個常量的抽象方法。

示例:

enum EType{
      ONE{
            void sayHello() {System.out.println("I'm one");}
      },
      TWO{
            void sayHello() {System.out.println("I'm two");}
      },
      THREE{
            void sayHello() {System.out.println("I'm three");}
      };

      Object VALUES;

      EType(){
            this(3);
      }

      EType(int i){
            this.VALUES = i;
      }
      abstract void sayHello();
}

參考文獻

JCP 的Enum規範

https://www.jcp.org/en/jsr/detail?id=161
https://jcp.org/aboutJava/communityprocess/jsr/tiger/enum.html

JDK1.8Enum原始碼

Effective Java(第二版) 第六章

相關文章