java中列舉的作用和用法
列舉(enum),是指一個經過排序的、被打包成一個單一實體的項列表。一個列舉的例項可以使用列舉項列表中任意單一項的值。列舉在各個語言當中都有著廣泛的應用,通常用來表示諸如顏色、方式、類別、狀態等等數目有限、形式離散、表達又極為明確的量。Java從JDK5開始,引入了對列舉的支援。
在枚舉出現之前,如果想要表示一組特定的離散值,往往使用一些常量。例如:
- package com.fhp.enumexample;
- public class Entity {
- public static final int VIDEO = 1;//視訊
- public static final int AUDIO = 2;//音訊
- public static final int TEXT = 3;//文字
- public static final int IMAGE = 4;//圖片
- private int id;
- private int type;
- public int getId() {
- return id;
- }
- public void setId(int id) {
- this.id = id;
- }
- public int getType() {
- return type;
- }
- public void setType(int type) {
- this.type = type;
- }
- }
當然,常量也不僅僅侷限於int型,諸如char和String等也是不在少數。然而,無論使用什麼樣的型別,這樣做都有很多的壞處。這些常量通常都是連續、有無窮多個值的量,而類似這種表示類別的量則是離散的,並且通常情況下只有有限個值。用連續的量去表示離散量,會產生很多問題。例如,針對上述的Entity類,如果要對Entity物件的type屬性進行賦值,一般會採用如下方法:
- Entity e = new Entity();
- e.setId(10);
- e.setType(2);
這樣做的缺點有:(1)程式碼可讀性差、易用性低。由於setType()方法的引數是int型的,在閱讀程式碼的時候往往會讓讀者感到一頭霧水,根本不明白這個2到底是什麼意思,代表的是什麼型別。當然,要保證可讀性,還有這樣一個辦法:
e.setType(Entity.AUDIO);
而這樣的話,問題又來了。這樣做,客戶端必須對這些常量去建立理解,才能瞭解如何去使用這個東西。說白了,在呼叫的時候,如果使用者不到Entity類中去看看,還真不知道這個引數應該怎麼傳、怎麼調。像是setType(2)這種用法也是在所難免,因為它完全合法,不是每個人都能夠建立起用常量名代替數值,從而增加程式可讀性、降低耦合性的意識。(2)型別不安全。在使用者去呼叫的時候,必須保證型別完全一致,同時取值範圍也要正確。像是setType(-1)這樣的呼叫是合法的,但它並不合理,今後會為程式帶來種種問題。也許你會說,加一個有效性驗證嘛,但是,這樣做的話,又會引出下面的第(3)個問題。
(3)耦合性高,擴充套件性差。假如,因為某些原因,需要修改Entity類中常量的值,那麼,所有用到這些常量的程式碼也就都需要修改——當然,要仔細地修改,萬一漏了一個,那可不是開玩笑的。同時,這樣做也不利於擴充套件。例如,假如針對類別做了一個有效性驗證,如果類別增加了或者有所變動,則有效性驗證也需要做對應的修改,不利於後期維護。
列舉就是為了這樣的問題而誕生的。它們給出了將一個任意項同另一個項相比較的能力,並且可以在一個已定義項列表中進行迭代。列舉(在Jave中簡稱為enum)是一個特定型別的類。所有列舉都是Java中的新類java.lang.Enum的隱式子類。此類不能手工進行子類定義。一個簡單的列舉可以是這樣:
- package com.fhp.enumexample;
- public enum TypeEnum {
- VIDEO, AUDIO, TEXT, IMAGE
- }
上面的Entity類就可以改成這樣:
- package com.fhp.enumexample;
- public class Entity {
- private int id;
- private TypeEnum type;
- public int getId() {
- return id;
- }
- public void setId(int id) {
- this.id = id;
- }
- public TypeEnum getType() {
- return type;
- }
- public void setType(TypeEnum type) {
- this.type = type;
- }
- }
在為Entity物件賦值的時候,就可以這樣:
- Entity e = new Entity();
- e.setId(10);
- e.setType(TypeEnum.AUDIO);
怎麼看,都是好了很多。在呼叫setType()時,可選值只有四個,否則會出現編譯錯誤,因此可以看出,列舉是型別安全的,不會出現取值範圍錯誤的問題。同時,客戶端不需要建立對列舉中常量值的瞭解,使用起來很方便,並且可以容易地對列舉進行修改,而無需修改客戶端。如果常量從列舉中被刪除了,那麼客戶端將會失敗並且將會收到一個錯誤訊息。列舉中的常量名稱可以被列印,因此除了僅僅得到列表中項的序號外還可以獲取更多資訊。這也意味著常量可用作集合的名稱,例如HashMap。
因為在Java中一個列舉就是一個類,它也可以有屬性和方法,並且實現介面。只是所有的列舉都繼承自java.lang.Enum類,因此enum不可以再繼承其他的類。
下面給出在列舉中宣告屬性和方法的示例:
- package com.fhp.enumexample;
- public enum TypeEnum {
- VIDEO(1), AUDIO(2), TEXT(3), IMAGE(4);
- int value;
- TypeEnum(int value) {
- this.value = value;
- }
- public int getValue() {
- return value;
- }
- }
在這個列舉中,每個列舉的值都有一個對應的int型欄位,而且不同的列舉值也會有不同的int數值。同時,它和普通的類一樣,可以宣告構造器和各種各樣的方法。如:
- TypeEnum type = TypeEnum.TEXT;//type的value屬性值為3。
- System.out.println(type.getValue());//螢幕輸出3。
如果要為每個列舉值指定屬性,則在列舉中必須宣告一個引數為屬性對應型別的構造方法(不能是public)。否則編譯器將給出The constructor TypeEnum(int, String) is undefined的錯誤。在此例中,屬性為int型,因此構造方法應當為int型。除此之外,還可以為列舉指定多個屬性,如:
- package com.fhp.enumexample;
- public enum TypeEnum {
- VIDEO(1, "視訊"), AUDIO(2, "音訊"), TEXT(3, "文字"), IMAGE(4, "影象");
- int value;
- String name;
- TypeEnum(int value, String name) {
- this.value = value;
- this.name = name;
- }
- public int getValue() {
- return value;
- }
- public String getName() {
- return name;
- }
- }
enum還內建了許多方法,常用的如下:
int compareTo(E o) 比較此列舉與指定物件的順序。 Class<E> getDeclaringClass() 返回與此列舉常量的列舉型別相對應的 Class 物件。 String name() 返回此列舉常量的名稱,在其列舉宣告中對其進行宣告。 int ordinal() 返回列舉常量的序數(它在列舉宣告中的位置,其中初始常量序數為零)。 String toString() 返回列舉常量的名稱,它包含在宣告中。 static <T extends Enum<T>> T valueOf(Class<T> enumType, String name) 返回帶指定名稱的指定列舉型別的列舉常量。
static T[] values()
返回該列舉的所有值。
現在,假設要為該列舉實現一個根據整數值生成列舉值的方法,可以這樣做:
- package com.fhp.enumexample;
- public enum TypeEnum {
- VIDEO(1, "視訊"), AUDIO(2, "音訊"), TEXT(3, "文字"), IMAGE(4, "影象");
- int value;
- String name;
- TypeEnum(int value, String name) {
- this.value = value;
- this.name = name;
- }
- public int getValue() {
- return value;
- }
- public String getName() {
- return name;
- }
- public static TypeEnum getByValue(int value) {
- for(TypeEnum typeEnum : TypeEnum.values()) {
- if(typeEnum.value == value) {
- return typeEnum;
- }
- }
- throw new IllegalArgumentException("No element matches " + value);
- }
- }
getByValue(int)即為整數值轉列舉值的方法。呼叫values()方法獲取到該列舉下的所有值,然後遍歷該列舉下面的每個值和給定的整數是否匹配,若匹配直接返回,若無匹配值則丟擲IllegalArgumentException異常,表示引數不合法,兼有有效性驗證的作用。
綜上,我們可以看到,在JDK5中新引入的列舉完美地解決了之前通過常量來表示離散量所帶來的問題,大大加強了程式的可讀性、易用性和可維護性,並且在此基礎之上又進行了擴充套件,使之可以像類一樣去使用,更是為Java對離散量的表示上升了一個臺階。因此,如果在Java中需要表示諸如顏色、方式、類別、狀態等等數目有限、形式離散、表達又極為明確的量,應當儘量捨棄常量表示的做法,而將列舉作為首要的選擇。