|
|
觀察 瞭解java.lang.Enum類別 反編譯Action列舉的程式碼,可以看到還有個values()方法,這個方法會將內部維護Action列舉實例的陣列複製後傳回,如果你想要知道有哪些列舉成員,就可以使用這個方法,由於是複製品,因此改變傳回的陣列,並不會影響Action內部所維護的陣列。 列舉型態既然繼承自Enum的類別,除了由編譯器自動產生的private建構式之外,也可以自行定義建構式,條件是不得為公開(public)建構式,也不可以於建構式中呼叫super()。 來看個實際應用,先前談過ordinal的值,會是使用enum列舉的成員順序,數值由0開始,如果這不是你想要的順序呢?例如原本有個interface定義的列舉常數: public interface Priority {
int MAX = 10; int NORM = 5; int MIN = 1; } 若現在你想要使用enum重新定義列舉,但又必須與既存API搭配,也就是定義好的列舉實例,必須有個int值符合既存API的Priority值,這時怎麼辦?可以如下定義:
package cc.openhome; 在這邊建構式定義為private,在enum中呼叫建構式比較特別,直接在列舉成員後加上括號,就可以指定建構式需要的引數,由於Enum的ordinal()被宣告為final,不能重新定義,所以自定義了value()方法來傳回int值。執行結果如下所示:
可以看看Priority.class反編譯後的結果: public final class Priority extends Enum {
...略 private Priority(String s, int i, int value) { super(s, i); this.value = value; } public int value() { return value; } ...略 public static final Priority MAX; public static final Priority NORM; public static final Priority MIN; private int value; private static final Priority $VALUES[]; static { MAX = new Priority("MAX", 0, 10); NORM = new Priority("NORM", 1, 5); MIN = new Priority("MIN", 2, 1); $VALUES = (new Priority[] { MAX, NORM, MIN }); } } 實際上你定義的建構式只是編譯器用來產生真正建構式時參考之用,你定義的value參數,編譯器會放在真正建構式的name與ordinal之後,真正的 建構式會呼叫super()時傳入name與ordinal(所以你不可以在自定義建構式中呼叫super()),接著才是自定義建構式中的程式碼。在 static區塊中,編譯器仍自行維護name與ordinal的值,接著才是你呼叫自定義建構式時傳入的value值。 定義列舉時還可以實作介面,例如有個介面定義如下:
package cc.openhome; 若要在定義列舉時實作Command介面,基本方式可以如下: public enum Action3 implements Command {
STOP, RIGHT, LEFT, UP, DOWN; public void execute() { switch(this) { case STOP: System.out.println("播放停止動畫"); break; case RIGHT: System.out.println("播放向右動畫"); break; case LEFT: System.out.println("播放向左動畫"); break; case UP: System.out.println("播放向上動畫"); break; case DOWN: System.out.println("播放向下動畫"); break; } } } 基本上就是使用enum定義列舉時,使用implements實作介面,並將介面定義的方法實作,就如同定義class時使用implements實作介面。 不過如果在實作介面,希望各列舉成員可以有不同實作,例如上面程式片段中,其實你想讓列舉成員不僅有各自列舉實例,還可以帶有各自的可執行指令,也就是希望可以如下執行程式:
package cc.openhome; 希望可以有以下的執行結果:
為了這個目的,先前實作Command時的execute()方法時,是使用switch比對列舉實例,但其實可以有更好的作法,就是定義enum時有個特定值類別本體(Value-Specific Class Bodies)語法,直接來看如何運用此語法:
package cc.openhome; 可以看到在列舉成員後,直接加上{}實作Command的execute()方法,這代表著每個列舉實例都會有不同的execute()實作,在職責分配上,比switch的方式清楚許多。 實際上,編譯器會將Action標示為抽象類別: public abstract class Action extends Enum implements Command {
... } 並為每個列舉成員後的{}語法,產生匿名內部類別,這個匿名內部類別繼承了Action,實作了execute()方法: ...略
static { STOP = new Action3("STOP", 0) { public void execute() { System.out.println("\u64AD\u653E\u505C\u6B62\u52D5\u756B"); } }; RIGHT = new Action3("STOP", 0) { public void execute() { System.out.println("\u64AD\u653E\u505C\u6B62\u52D5\u756B"); } }; ...略 } ...略 所以每個列舉成員,實際上都參考至Action的匿名子類別。瞭解這個原理後,也就可以知道,特定值類別本體語法不僅在實作介面時可以使用,也可以運用在重新定義父類別方法。例如重新定義toString(),以先前Priority為例,可改寫為以下:
package cc.openhome; 執行結果如下:
|