1. 程式人生 > >java中列舉型別Enum的用法

java中列舉型別Enum的用法

以前對Enum只有一個模糊的概念,最近專案中要用,所以就專門研究了一下:

java的Enum列舉型別終於在j2se1.5出現了。之前覺得它只不過是雞肋而已,可有可無。畢竟這麼多年來,沒有它,大家不都過得很好嗎?今日看《Thinking in Java》4th edition,裡面有一句話“有時恰恰因為它,你才能夠"優雅而乾淨"地解決問題。優雅與清晰很重要,正式它們區別了成功的解決方案與失敗的解決方案。而失敗的解決方案就是因為其他人無法理他。"使用Enum列舉型別,可以將以前笨拙的程式碼變得優雅簡單?但是,我同時也在思考另外一個問題,使用新的技術,會不會給技術人員帶來更多的負擔呢?

"學習新版語言的一個危險就是瘋狂使用新的語法結構"

        Enum作為Sun全新引進的一個關鍵字,看起來很象是特殊的class, 它也可以有自己的變數,可以定義自己的方法,可以實現一個或者多個介面。 當我們在宣告一個enum型別時,我們應該注意到enum型別有如下的一些特徵。

1.它不能有public的建構函式,這樣做可以保證客戶程式碼沒有辦法新建一個enum的例項。

2.所有列舉值都是public , static , final的。注意這一點只是針對於列舉值,我們可以和在普通類裡面定義 變數一樣定義其它任何型別的非列舉變數,這些變數可以用任何你想用的修飾符。

3.Enum預設實現了java.lang.Comparable介面。 

4.Enum覆載了了toString方法,因此我們如果呼叫Color.Blue.toString()預設返回字串”Blue”.

5.Enum提供了一個valueOf方法,這個方法和toString方法是相對應的。呼叫valueOf(“Blue”)將返回Color.Blue.因此我們在自己重寫toString方法的時候就要注意到這一點,一把來說應該相對應地重寫valueOf方法。

6.Enum還提供了values方法,這個方法使你能夠方便的遍歷所有的列舉值。

7.Enum還有一個oridinal的方法,這個方法返回列舉值在列舉類種的順序,這個順序根據列舉值宣告的順序而定,這裡Color.Red.ordinal()返回0。

瞭解了這些基本特性,我們來看看如何使用它們。

1.遍歷所有有列舉值. 知道了有values方法,我們可以輕車熟路地用ForEach迴圈來遍歷了列舉值了。

for(Color    c:    Color.values())     
  System.out.println(“find    value:”    +    c);     

2.在enum中定義方法和變數,比如我們可以為Color增加一個方法隨機返回一個顏色。 

public    enum    Color    {     
     Red,     
     Green,     
     Blue;     
    
     /*     
     *定義一個變量表示列舉值的數目。     
     *(我有點奇怪為什麼sun沒有給enum直接提供一個size方法).     
     */     
     private    static    int    number    =    Color.values().length    ;     
    
     /**     
     *    隨機返回一個列舉值     
     @return    a    random    enum    value.     
     */     
     public    static    Color    getRandomColor(){     
     long    random    =    System.currentTimeMillis()    %    number;     
     switch    ((int)    random){     
      case    0:     
       return    Color.Red;     
      case    1:     
       return    Color.Green;     
      case    2:     
       return    Color.Blue;     
      default    :    return    Color.Red;     
     }     
     }     
     }     

可以看出這在列舉型別裡定義變數和方法和在普通類裡面定義方法和變數沒有什麼區別。唯一要注意的只是變數和方法定義必須放在所有列舉值定義的後面,否則編譯器會給出一個錯誤。
3.覆載(Override)toString, valueOf方法 前面我們已經知道enum提供了toString,valueOf等方法,很多時候我們都需要覆載預設的toString方法,那麼對於enum我們怎麼做呢。其實這和覆載一個普通class的toString方法沒有什麼區別。

 public    String    toString(){     
     switch    (this){     
     case    Red:     
      return    "Color.Red";     
     case    Green:     
      return    "Color.Green";     
     case    Blue:     
      return    "Color.Blue";     
     default:     
      return    "Unknow    Color";     
     }     
     }     

這時我們可以看到,此時再用前面的遍歷程式碼打印出來的是

Color.Red

Color.Green

Color.Blue

而不是

Red

Green

Blue.

可以看到toString確實是被覆載了。一般來說在覆載toString的時候我們同時也應該覆載valueOf方法,以保持它們相互的一致性。

4.使用建構函式 雖然enum不可以有public的建構函式,但是我們還是可以定義private的建構函式,在enum內部使用。還是用Color這個例子。

 public    enum    Color    {     
     Red("This    is    Red"),     
     Green("This    is    Green"),     
     Blue("This    is    Blue");     
    
     private    String    desc;     
    
     Color(String    desc){     
     this.desc    =    desc;     
     }     
    
     public    String    getDesc(){     
     return    this.desc;     
     }     
    
     }     


這裡我們為每一個顏色提供了一個說明資訊, 然後定義了一個建構函式接受這個說明資訊。 要注意這裡建構函式不能為public或者protected, 從而保證建構函式只能在內部使用,客戶程式碼不能new一個列舉值的例項出來。這也是完全符合情理的,因為我們知道列舉值是public static final的常量而已。

5.實現特定的介面 我們已經知道enum可以定義變數和方法,它要實現一個介面也和普通class實現一個介面一樣,這裡就不作示例了。

6.定義列舉值自己的方法。 前面我們看到可以為enum定義一些方法,其實我們甚至可以為每一個列舉值定義方法。這樣,我們前面覆載 toString的例子可以被改寫成這樣。

public    enum    Color    {     
     Red    {     
     public    String    toString(){     
      return    "Color.Red";     
     }     
     },     
     Green    {     
     public    String    toString(){     
      return    "Color.Green";     
     }     
     },     
     Blue{     
     public    String    toString(){     
      return    "Color.Blue";     
     }     
     };     
     }     

從邏輯上來說這樣比原先提供一個“全域性“的toString方法要清晰一些。 總的來說,enum作為一個全新定義的型別,是希望能夠幫助程式設計師寫出的程式碼更加簡單易懂,個人覺得一般也不需要過多的使用enum的一些高階特性,否則就和簡單易懂的初衷想違背了。

先學習一下enum的簡單應用,以下簡潔的程式碼已經包括enum所提供的絕大部分功能。

1.enum的應用,包括定義,遍歷,switch,enumset,enummap等

package com.autonavi.test;

import java.util.EnumMap;

import java.util.EnumSet;

/**
 * Java列舉型別enum使用詳解
 * MyEnum可以存放多個列舉型別
 * 列舉型別也可以直接以類的形式宣告,可以跟類、介面同級別
 * public enum State{ON,OFF } 單獨可以作為一個java檔案
 * @author heqingfei
 */

public class MyEnum {
	// 定義一個enum列舉型別,包括例項ON,OFF
	public enum State {
		ON, OFF
	}

	public enum country {
		China, Japan
	}

	// 測試方法
	public static void main(String[] args) {
		// 直接變數enum
		for (State s : State.values()) {
			System.out.println(s.name());
		}
		
		// 直接變數enum
		for (country c : country.values()) {
			System.out.println(c.name());
		}

		// switch與enum的結合使用
		State switchState = State.OFF;
		switch (switchState) {
		case OFF:
			System.out.println("OFF");
			break;
		case ON:
			System.out.println("ON");
			break;
		}
		
		System.out.println("******EnumSet的用法********");
		// EnumSet的使用
		EnumSet<State> stateSet = EnumSet.allOf(State.class);
		for (State s : stateSet) {
			System.out.println(s);
		}
		
		System.out.println("******EnumMap的用法********");
		//EnumMap的使用
		EnumMap stateMap = new EnumMap(State.class);
		stateMap.put(State.ON, "is On");
		stateMap.put(State.OFF, "is off");
		for (State s : State.values()) {
			System.out.println(s.name() + ":" + stateMap.get(s));
		}

		System.out.println("===================");
		// 列舉可以這樣被呼叫
		State state1 = MyEnum.State.OFF;
		System.out.println(state1.name());
		System.out.println("===================");
		State[] state2 = MyEnum.State.values();
		for (State state : state2) {
			System.out.println(state.name());
		}
	}

}

以下內容可能有些無聊,但絕對值得一窺
1.
public class State {
public static final int ON = 1;
public static final Int OFF= 0;
}

有什麼不好了,大家都這樣用了很長時間了,沒什麼問題啊。
首先,它不是型別安全的。你必須確保是int
其次,你還要確保它的範圍是0和1
最後,很多時候你打印出來的時候,你只看到 1 和0 ,

但其沒有看到程式碼的人並不知道你的企圖
so,拋棄你所有舊的public static final常量吧

2.可以建立一個enum類,把它看做一個普通的類。除了它不能繼承其他類了。(java是單繼承,它已經繼承了Enum),
可以新增其他方法,覆蓋它本身的方法

3.switch()引數可以使用enum了

4.values()方法是編譯器插入到enum定義中的static方法,所以,當你將enum例項向上轉型為父類Enum是,values()就不可訪問了。解決辦法:在Class中有一個getEnumConstants()方法,所以即便Enum介面中沒有values()方法,我們仍然可以通過Class物件取得所有的enum例項

5.無法從enum繼承子類,如果需要擴充套件enum中的元素,在一個介面的內部,建立實現該介面的列舉,以此將元素進行分組。達到將列舉元素進行分組。

6.使用EnumSet代替標誌。enum要求其成員都是唯一的,但是enum中不能刪除新增元素。

7.EnumMap的key是enum,value是任何其他Object物件。

8.enum允許程式設計師為eunm例項編寫方法。所以可以為每個enum例項賦予各自不同的行為。

9.使用enum的職責鏈(Chain of Responsibility) .這個關係到設計模式的職責鏈模式。以多種不同的方法來解決一個問題。然後將他們連結在一起。當一個請求到來時,遍歷這個鏈,直到鏈中的某個解決方案能夠處理該請求。

10.使用enum的狀態機

11.使用enum多路分發

用法一:常量

JDK1.5 之前,我們定義常量都是:public static fianl....。現在好了,有了列舉,可以把相關的常量分組到一個列舉型別裡,而且列舉提供了比常量更多的方法。

public enum Color {
  RED, GREEN, BLANK, YELLOW
}

用法二:switch

JDK1.6之前的switch語句只支援int,char,enum型別,使用列舉,能讓我們的程式碼可讀性更強。

enum Signal {
	GREEN, YELLOW, RED
}
public class TrafficLight {
	Signal color = Signal.RED;
	public void change() {
		switch (color) {
		case RED:
			color = Signal.GREEN;
			break;
		case YELLOW:
			color = Signal.RED;
			break;
		case GREEN:
			color = Signal.YELLOW;
			break;
		}
	}
}

用法三:向列舉中新增新方法

如果打算自定義自己的方法,那麼必須在enum例項序列的最後新增一個分號。而且 Java 要求必須先定義 enum例項。

public enum Color {
	RED("紅色", 1), GREEN("綠色", 2), BLANK("白色", 3), YELLO("黃色", 4);
	// 成員變數
	private String name;
	private int index;
	// 構造方法
	private Color(String name, int index) {
		this.name = name;
		this.index = index;
	}
	// 普通方法
	public static String getName(int index) {
		for (Color c : Color.values()) {
			if (c.getIndex() == index) {
				return c.name;
			}
		}
		return null;
	}
	// get set 方法
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getIndex() {
		return index;
	}
	public void setIndex(int index) {
		this.index = index;
	}
}

用法四:覆蓋列舉的方法

下面給出一個toString()方法覆蓋的例子。

public enum Color {
	RED("紅色", 1), GREEN("綠色", 2), BLANK("白色", 3), YELLO("黃色", 4);
	// 成員變數
	private String name;
	private int index;
	// 構造方法
	private Color(String name, int index) {
		this.name = name;
		this.index = index;
	}
	//覆蓋方法
	@Override
	public String toString() {
		return this.index+"_"+this.name;
	}
}

用法五:實現介面

所有的列舉都繼承自java.lang.Enum類。由於Java 不支援多繼承,所以列舉物件不能再繼承其他類。

public interface Behaviour {
	void print();
	String getInfo();
}
public enum Color implements Behaviour{
	RED("紅色", 1), GREEN("綠色", 2), BLANK("白色", 3), YELLO("黃色", 4);
	// 成員變數
	private String name;
	private int index;
	// 構造方法
	private Color(String name, int index) {
		this.name = name;
		this.index = index;
	}
//介面方法
	@Override
	public String getInfo() {
		return this.name;
	}
	//介面方法
	@Override
	public void print() {
		System.out.println(this.index+":"+this.name);
	}
}


用法六:使用介面組織列舉

public interface Food {
	enum Coffee implements Food{
		BLACK_COFFEE,DECAF_COFFEE,LATTE,CAPPUCCINO
	}
	enum Dessert implements Food{
		FRUIT, CAKE, GELATO
	}
}

用法七:關於列舉集合的使用

java.util.EnumSet和java.util.EnumMap是兩個列舉集合。EnumSet保證集合中的元素不重複;EnumMap中的 key是enum型別,而value則可以是任意型別。關於這個兩個集合的使用就不在這裡贅述,可以參考JDK文件。

然後專門寫一個工具類,用來對列舉型別進行編碼和解碼:

private static String[] showCollectType = {"服務採集", "效能採集"};
/**
	 * 將採集型別編碼
	 * @param collectType
	 * @return
	 */
	public static CollectType encodeCollectType(String collectType) {
		CollectType collType = CollectType.COLLSERVICE;
		if(collectType.equals("服務採集")) {
			collType = CollectType.COLLSERVICE;
		} else if(collectType.equals("效能採集")) {
			collType = CollectType.COLLPERFORM;
		}
		return collType;
	}
/**
	 * 將採集型別解碼
	 * @param collType
	 * @return
	 */
	public static String decodeCollectType(CollectType collType) {
		String collectType = "服務採集";
		if(collType.equals(CollectType.COLLSERVICE)) {
			collectType = "服務採集";
		} else if(collType.equals(CollectType.COLLPERFORM)) {
			collectType = "效能採集";
		}
		return collectType;
	}
/**
  * 獲得採集型別
  */
 public static String[] getShowCollectType() {
  return showCollectType;
 }