Java枚舉類型


     關鍵字enum可以將一組具名的值的有限集合創建為一種新的類型,而這些具名的值可以作為常規的程序組件使用。這些具名的值稱為枚舉值,這種新的類型稱為枚舉類型。

     下面是一個簡單的表示星期幾的枚舉:

1 public enum Day {
2     SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY
3 }

     在創建enum時,編譯器會自動添加一些有用的特性。比如創建toString()方法以便顯示某個enum實例的名字;創建ordinal()方法表示某個特定enum常量的申明順序;values()方法用來按照enum常量的申明順序產生這些常量構成的數組。enum看起來像是一種新的數據類型,除了編譯上面這些特殊的編譯行為,很大程度上可以將enum看成是一個普通的類來處理。這些內容在后面會有詳細的介紹。

public static void main(String[] args) {
    System.out.println(Day.class.getSuperclass());
    for (Day day : Day.values()) {
        System.out.println(day.name() + " ordinal: " + day.ordinal());
    }
}

class java.lang.Enum

SUNDAY ordinal: 0

MONDAY ordinal: 1

TUESDAY ordinal: 2

WEDNESDAY ordinal: 3

THURSDAY ordinal: 4

FRIDAY ordinal: 5

SATURDAY ordinal: 6

     上面的代碼中輸出了枚舉類型Day的父類及演示了values()、name()、ordinal()三個方法的調用。從輸出結果中可以看到Day的父類是java.lang.Enum,但是在定義Day的時候並沒有通過extends指明繼承java.lang.Enum,也不需要通過extends關鍵字指定。當創建enum時編譯器會生成一個相關的類,這個類會繼承java.lang.Enum。既然枚舉實例已經集成了java.lang.Enum,Java又不支持多繼承,所以enum不能再繼承其他的類,但是能實現接口。ordinal()方法返回一個int值,這是每個enum實例在申明時的次序,從0開始。

     除了不能繼承自一個enum外,基本上可以將enum看做一個常規的類。也就是說可以向enum中添加方法,比如返回enum自身描述的方法,還可以添加main方法。下面是一個演示enum添加自定義方法和實現接口的例子。

 1 public enum Signal implements ObjectDescription {
 2     Red("紅燈", "敢過去就是6分,還要罰款哦"), 
 3     Yellow("黃燈", "黃燈你也不敢過"), 
 4     Green("綠燈", "綠燈也得小心過啊");
 5 
 6     private String name;
 7     private String description;
 8 
 9     private Signal(String name, String description) {
10         this.name = name;
11         this.description = description;
12     }
13 
14     private String getName() {
15         return name;
16     }
17 
18     private String getDescription() {
19         return description;
20     }
21 
22     @Override
23     public String todo() {
24         return "Signal類用於表示交通信號指示燈," + this + "用於表示" + this.getName();
25     }
26 
27     public static void main(String[] args) {
28         for (Signal signal : Signal.values()) {
29             System.out.println(signal.todo());
30         }
31         for (Signal signal : Signal.values()) {
32             System.out.println(signal.getName() + ": "
33                     + signal.getDescription());
34         }
35     }
36 
37 }

     注意:如果要自定義方法,那么必須在enum實例序列的最后添加一個分號。同時,Java要求必須先定義enum實例,否則編譯時會報錯。

     enum的構造器無論是不是private,都只能在enum定義的內部使用來創建enum實例,一旦enum的定義結束,編譯器就不允許再使用其構造器來創建任何實例了。

 

使用接口組織枚舉

     無法使用繼承限制了枚舉的使用,比如需要用enum表示食物,但同時必須分為水果,點心等類型的時候就沒那么方便的滿足了。

     下面通過在一個接口內部創建實現該接口的枚舉,從而達到對元素進行分類組織的目的。

 1 public interface Food {
 2     enum Appetizer implements Food {
 3         SALAD, SOUP, SPRING_ROLLS;
 4     }
 5 
 6     enum MainCourse implements Food {
 7         LASAGNE, BURRITO, PAD_THAI, LENTILS, HUMMOUS, VINDALOO;
 8     }
 9 
10     enum Dessert implements Food {
11         TIRAMISU, GELATO, BLACK_fOREST_CAKE, FRUIT;
12     }
13 
14     enum Coffee implements Food {
15         BLACK_COFFEE, DECAF_COFFEE, LATTE;
16     }
17 }

     對於enum而言,實現接口是使其子類化的唯一辦法。通過上面的形式,成功的對不同的食物進行分組,但都是Food。 

 

枚舉的枚舉

     下面是一個枚舉的隨機選擇器,是一個工具類。

 1 public class Enums {
 2     private static Random rand = new Random(47);
 3 
 4     public static <T extends Enum<T>> T randrom(Class<T> ec) {
 5         return random(ec.getEnumConstants());
 6     }
 7 
 8     public static <T> T random(T[] values) {
 9         return values[rand.nextInt(values.length)];
10     }
11 }

     結合工具類及上面Food接口的內容,下面使用枚舉的枚舉實現一個產生隨機菜單的例子。

 1 public enum Course {
 2     APPETIZER(Food.Appetizer.class), MAINCOURSE(Food.MainCourse.class), DESSERT(
 3             Food.Dessert.class), COFFEE(Food.Coffee.class);
 4     private Food[] values;
 5 
 6     private Course(Class<? extends Food> kind) {
 7         // 返回枚舉中所有的元素,及所有實例構成的數組,如果kind不是枚舉返回null
 8         values = kind.getEnumConstants();
 9     }
10 
11     public Food randomSelection() {
12         return Enums.random(values);
13     }
14 
15     public static void main(String[] args) {
16         // 產生5份隨機菜單
17         for (int i = 0; i < 5; i++) {
18             for (Course c : Course.values()) {
19                 Food food = c.randomSelection();
20                 System.out.println(food);
21             }
22             System.out.println("--------------------");
23         }
24     }
25 }

     下面給出一個驗證values()方法不是通過父類繼承的方法。

 1 public enum Signal implements ObjectDescription {
 2     Red("紅燈", "敢過去就是6分,還要罰款哦"), Yellow("黃燈", "黃燈你也不敢過"), Green("綠燈", "綠燈也得小心過啊");
 3 
 4     private String name;
 5     private String description;
 6 
 7     private Signal(String name, String description) {
 8         this.name = name;
 9         this.description = description;
10     }
11 
12     private String getName() {
13         return name;
14     }
15 
16     private String getDescription() {
17         return description;
18     }
19 
20     @Override
21     public String todo() {
22         return "Signal類用於表示交通信號指示燈," + this + "用於表示" + this.getName();
23     }
24 
25     public static void main(String[] args) {
26         Set<Method> methodSet = new HashSet<Method>();
27         Method[] signalMethods = Signal.class.getMethods();
28         for (Method m : signalMethods) {
29             methodSet.add(m);
30         }
31         Method[] superClassMethods = Signal.class.getSuperclass().getMethods();
32         for (Method m : superClassMethods) {
33             methodSet.remove(m);
34         }
35         System.out.println(methodSet);
36     }
37 
38 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM