1. 程式人生 > >Java列舉類(6.9)

Java列舉類(6.9)

參考《java瘋狂講義》

1. 列舉類入門
java 5 新增enum關鍵字,用以定義列舉類。
它與普通類的區別如下:

  • 列舉類可以實現一個或多個介面,使用enum定義的列舉類預設繼承了java.lang.Enum類,而不是預設繼承Object類,因此列舉類不能顯式繼承繼承其他父類。其中java.lang.Enum類實現了java.lang.Serializable和java.lang.Comparable介面。
  • 使用enum定義,非抽象的列舉類預設使用final修飾,因此列舉類不能派生子類。
  • 列舉類的構造器只能使用private修飾,如果省略構造器的訪問控制符,則預設使用private修飾。
  • 列舉類的所有例項必須在列舉類的第一行顯式列出,否則這個類永遠不能產生例項。列出這些例項時,系統預設會加上public static final修飾符,無需程式設計師顯式新增。
  • 列舉類預設提供了一個values()方法,該方法可以很方便的遍歷所有列舉值。
public enum SeasonEnum
{
   // 在第一行列出4個列舉值   
   SPRING,SUMMER,FALL,WINTER;
}

如果需要使用列舉類的某個例項,可以使用EnumClass.variable的形式。

public class EnumTest
{
   public void judge(SeasonEnum s)
   {
      // switch語句裡的表示式可以時列舉值
      switch (s)
      {
         case SPRING:
            System.out.println("春暖花開,正好踏青");
            break;
         case SUMMER:
            System.out.println("夏日炎炎,適合游泳");
            break;
         case FALL:
            System.out.println("秋高氣爽,進補及時");
            break;
         case WINTER:
            System.out.println("冬日雪飄,圍爐賞雪");
            break;
      }
   }
   public static void main(String[] args)
   {
      // 列舉類預設有一個values方法,返回該列舉類的所有例項
      for (SeasonEnum s : SeasonEnum.values())
      {
         System.out.println(s);
      }
      // 使用列舉例項時,可通過EnumClass.variable的形式訪問
      new EnumTest().judge(SeasonEnum.SPRING);
   }
}

switch的控制表示式可以是任何列舉型別。不僅如此,當switch控制表示式使用列舉型別時,後面case表示式中的值直接使用列舉值的名字,無需新增列舉類作為限定。

所有的列舉類都繼承了java.lang.Enum類,所以所有列舉類都可以直接使用java.lang.Enum類的方法。java.lang.Enum類提供瞭如下幾個常用方法:

  • int compareTo(E o):該方法用於指定列舉物件的比較順序,同一個列舉例項只能與相同型別的列舉例項進行比較。若該列舉物件在指定列舉物件之後,返回正整數,之後返回負整數,否則返回0
  • String name():返回此列舉型別的名稱,用的不多,更多采用下面的方法,因為其返回更加使用者友好的名稱。
  • String toString():返回列舉常量的名稱
  • int ordinal():返回列舉值在列舉類中的索引值(就是在宣告中的位置,索引值從0開始)
  • public static <T extends Enum>T valueOf(ClassenumType,String name):用於返回指定列舉類中指定名稱的列舉值。名稱必須與在該列舉類中宣告列舉值時所用的識別符號完全匹配,不允許使用額外的空白字元。

2. 列舉類的成員變數,方法和構造器

public enum Gender
{
   MALE,FEMALE;
   // 定義一個public修飾的例項變數
   public String name;
}

public class GenderTest
{
   public static void main(String[] args)
   {
      //通過Enum的valueOf()方法來獲取指定列舉型別的列舉值
      Gender g = Enum.valueOf(Gender.class , "FEMALE");
      //直接為列舉類的name例項變數賦值
      g.name = "女";
      //直接訪問列舉值的name例項變數
      System.out.println(g + "代表:" + g.name);
   }
}

為了保護列舉類的成員變數,列舉類通常應該設計成不可變類,也就是說,它的成員變數值因該不允許被改變,因此建議將列舉類的成員變數用private final修飾。如果將所有成員變數都用final修飾符,那麼就必須在構造器裡為這些成員變數賦初值(在定義時,或者初始化塊中指定初始值也可以,不過比較少見)因此應該為列舉類顯示定義帶參的構造器。

public enum Gender
{
   // 此處的列舉值必須呼叫對應的構造器來建立
   MALE("男"),FEMALE("女");
   private final String name;
   // 列舉類的構造器只能使用private修飾
   private Gender(String name)
   {
      this.name = name;
   }
   public String getName()
   {
      return this.name;
   }
}

一旦為列舉類定義了帶引數的構造器,列出列舉值時就必須對應的傳入引數。也就是說,在列舉類中列出列舉值時,實際上就是呼叫構造器建立列舉類物件,只是這裡無需使用new關鍵字和顯式呼叫構造器,。前面列出列舉值時無需傳入引數,甚至無需括號,僅僅是因為前面的列舉類包含無參的構造器。

3. 實現介面的列舉類
列舉類也可以實現一個或多個介面。與普通類實現一個或多個介面一樣,列舉類實現一個或多個介面時,也需要實現該介面所包含的抽象方法。

public enum Gender implements GenderDesc
{
   // 此處的列舉值必須呼叫對應的構造器來建立
   MALE("男")//②
   // 花括號部分實際上是一個類體部分
   {
      public void info()
      {
         System.out.println("這個列舉值代表男性");
      }
   },
   FEMALE("女")//②
   {
      public void info()
      {
         System.out.println("這個列舉值代表女性");
      }
   };
  private Gender(String name)
   {
      this.name = name;
   }
   public String getName()
   {
      return this.name;
   }
   public void info()//①
   {
      System.out.println(
         "這是一個用於定義性別的列舉類");
   }
}

如果由列舉類實現接口裡的方法,①則每個列舉值在呼叫該方法時都有相同的行為方式(因為方法體一樣)。如果需要每個列舉值在呼叫該方法時呈現出不同的行為方式,則可以讓每個列舉值分別來實現該方法,每個列舉值提供不同的方法體②

當建立兩個列舉值時,後面緊跟花括號,裡面包含了一個方法的定義。有人可能會有點疑問。其實花括號與前面的匿名內部類語法相似。在這種情況下,當建立一個列舉值時,並不是直接建立一個列舉例項,而是建立Gender的匿名子類的例項。
疑問:列舉類不是用final修飾嗎?怎麼還能派生子類。
並不是所有的列舉類都使用了final修飾,非抽象的列舉類才預設使用final修飾,對於一個抽象列舉類而言——只包含了抽象方法,他就是抽象列舉類,系統會預設使用abstract修飾,而不是final修飾。

4. 包含抽象方法的列舉類

public enum Operation
{
   PLUS
   {
      public double eval(double x , double y)
      {
         return x + y;
      }
   },
   MINUS
   {
      public double eval(double x , double y)
      {
         return x - y;
      }
   },
   TIMES
   {
      public double eval(double x , double y)
      {
         return x * y;
      }
   },
   DIVIDE
   {
      public double eval(double x , double y)
      {
         return x / y;
      }
   };
   // 為列舉類定義一個抽象方法
   // 這個抽象方法由不同的列舉值提供不同的實現
   public abstract double eval(double x, double y);
   public static void main(String[] args)
   {
      System.out.println(Operation.PLUS.eval(3, 4));
      System.out.println(Operation.MINUS.eval(5, 4));
      System.out.println(Operation.TIMES.eval(5, 4));
      System.out.println(Operation.DIVIDE.eval(5, 4));
   }
}

列舉類裡定義抽象方法時不能用abstract修飾符將列舉類定義成抽象類(因為系統自動會為它新增abstract),但因為列舉值需要顯示建立列舉值,而不是作為父類,所以定義每個列舉值時必須為抽象方法提供實現,否則將出現編譯錯誤。