觀察 瞭解 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;
public enum Priority {
MAX(10), NORM(5), MIN(1);
private int value;
private Priority(int value) {
this.value = value;
}
public int value() {
return value;
}
public static void main(String[] args) {
for(Priority priority : Priority.values()) {
System.out.printf("Priority(%s, %d)%n",
priority, priority.value());
}
}
}
在這邊建構式定義為private
,在enum
中呼叫建構式比較特別,直接在列舉成員後加上括號,就可以指定建構式需要的引數,由於Enum
的ordinal()
被宣告為final
,不能重新定義,所以自定義了value()
方法來傳回int值。執行結果如下所示:
Priority(MAX, 10)
Priority(NORM, 5)
Priority(MIN, 1)
Priority(NORM, 5)
Priority(MIN, 1)
可以看看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;
public interface Command {
void execute();
}
若要在定義列舉時實作
Command
介面,基本方式可以如下:import static java.lang.System.out;
public enum Action3 implements Command {
STOP, RIGHT, LEFT, UP, DOWN;
public void execute() {
switch(this) {
case STOP:
out.println("播放停止動畫");
break;
case RIGHT:
out.println("播放向右動畫");
break;
case LEFT:
out.println("播放向左動畫");
break;
case UP:
out.println("播放向上動畫");
break;
case DOWN:
out.println("播放向下動畫");
break;
}
}
}
基本上就是使用
enum
定義列舉時,使用implements
實作介面,並將介面定義的方法實作,就如同定義class
時使用implements
實作介面。不過如果在實作介面,希望各列舉成員可以有不同實作,例如上面程式片段中,其實你想讓列舉成員不僅有各自列舉實例,還可以帶有各自的可執行指令,也就是希望可以如下執行程式:
package cc.openhome;
public class Game3 {
public static void main(String[] args) {
Game3.play(Action3.RIGHT);
Game3.play(Action3.DOWN);
}
static void play(Action3 action) {
action.execute();
}
}
你希望可以有以下的執行結果:
播放右轉動畫
播放向下動畫
播放向下動畫
為了這個目的,先前實作
Command
時的execute()
方法時,是使用switch
比對列舉實例,但其實可以有更好的作法,就是定義enum
時有個特定值類別本體(Value-Specific Class Bodies)語法,直接來看如何運用此語法:package cc.openhome;
import static java.lang.System.out;
public enum Action3 implements Command {
STOP {
public void execute() {
out.println("播放停止動畫");
}
},
RIGHT {
public void execute() {
out.println("播放右轉動畫");
}
},
LEFT {
public void execute() {
out.println("播放左轉動畫");
}
},
UP {
public void execute() {
out.println("播放向上動畫");
}
},
DOWN {
public void execute() {
out.println("播放向下動畫");
}
};
}
可以看到在列舉成員後,直接加上
{}
實作Command
的execute()
方法,這代表著每個列舉實例都會有不同的execute()
實作,在職責分配上,比switch
的方式清楚許多。實際上,編譯器會將
Action3
標示為抽象類別:public abstract class Action3 extends Enum implements Command {
...
}
並為每個列舉成員後的
{}
語法,產生匿名內部類別,這個匿名內部類別繼承了Action3
,實作了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");
}
};
...略
}
...略
所以每個列舉成員,實際上都參考至
Action3
的匿名子類別。瞭解這個原理後,也就可以知道,特定值類別本體語法不僅在實作介面時可以使用,也可以運用在重新定義父類別方法。例如重新定義toString()
,以先前Priority
為例,可改寫為以下:package cc.openhome;
import static java.lang.String.format;
public enum Priority2 {
MAX(10) {
public String toString() {
return format("(%2d) - 最大權限", value);
}
},
NORM(5) {
public String toString() {
return format("(%2d) - 普通權限", value);
}
},
MIN(1) {
public String toString() {
return format("(%2d) - 最小權限", value);
}
};
protected int value;
private Priority2(int value) {
this.value = value;
}
public int value() {
return value;
}
public static void main(String[] args) {
for(Priority2 priority : Priority2.values()) {
System.out.println(priority);
}
}
}
執行結果如下:
(10) - 最大權限
( 5) - 普通權限
( 1) - 最小權限
( 5) - 普通權限
( 1) - 最小權限