天天看點

淺談在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中需要表示諸如顔色、方式、類别、狀态等等數目有限、形式離散、表達又極為明确的量,應當盡量舍棄常量表示的做法,而将枚舉作為首要的選擇。