在 使用 enum
列舉常數 中使用enum
定義過以下的Action
列舉型態:
public enum Action {
STOP, RIGHT, LEFT, UP, DOWN
}
在當時談過,enum
定義了特殊的類別,繼承自java.lang.Enum
,不過這是由編譯器處理,直接撰寫程式繼承Enum
類別會被編譯器拒絕,即便如此,想要瞭解列舉型態如何定義與運用,先瞭解Enum
類別是必要的。首先看到Enum
的class
定義:
public abstract class Enum<E extends Enum<E>>
implements Comparable<E>, Serializable {
...
public final int compareTo(E o) {
Enum other = (Enum)o;
Enum self = this;
if (self.getClass() != other.getClass() && // optimization
self.getDeclaringClass() != other.getDeclaringClass())
throw new ClassCastException();
return self.ordinal - other.ordinal;
}
...
}
Enum
是個抽象類別,無法直接實例化,它實作了Comparable
介面,在compareTo()
方法中,主要是針對ordinal
成員比較,也就是在需要排序Enum
實例的場合,是依據ordinal
成員進行排序。ordinal
成員值是在Enum
建構式中設定:
...略
private final String name;
private final int ordinal;
protected Enum(String name, int ordinal) {
this.name = name;
this.ordinal = ordinal;
}
public final String name() {
return name;
}
public String toString() {
return name;
}
public final int ordinal() {
return ordinal;
}
...略
還記得 使用 enum
列舉常數 中曾列出Action.class反編譯後的內容嗎?以下再更詳細列出反編譯後的結果:
public final class Action extends Enum {
public static Action[] values() {
return (Action[])\$VALUES.clone();
}
public static Action valueOf(String s) {
return (Action)Enum.valueOf(Action, s);
}
private Action(String s, int i) {
super(s, i);
}
public static final Action STOP;
public static final Action RIGHT;
public static final Action LEFT;
public static final Action UP;
public static final Action DOWN;
略...
static {
STOP = new Action("STOP", 0);
RIGHT = new Action("RIGHT", 1);
LEFT = new Action("LEFT", 2);
UP = new Action("UP", 3);
DOWN = new Action("DOWN", 4);
略...
}
}
Action
的建構式被宣告為private
,因此只能在Action
類別中呼叫,呼叫建構式時,會傳入代表Action
列舉成員的名稱字串與int值,而在Action
建構式中呼叫了super()
,因此列舉成員的名稱字串與int
值會分別設定給Enum
的name
與ordinal
成員,因此ordinal
的值,會是使用enum
列舉的成員順序,數值由0開始。
可以透過Enum
定義的name()
方法取得列舉成員名稱字串,這適用於需要使用字串代表列舉值的場合,相當於toString()
的作用,事實上toString()
也只是傳回name
成員的值;可透過ordinal()
取得列舉int
值,這適用於需要使用int代表列舉值的場合。例如在JDK 1.4之前撰寫的API,仍是使用interface
定義常數作為列舉值,在使用enum
定義列舉之後,若仍想與這些舊API合作,就可以呼叫Enum
實例的ordinal()
方法。例如 使用 enum
列舉常數 的Game
類別,可以如下操作:
package cc.openhome;
public class GameDemo {
public static void main(String[] args) {
Game.play(Action2.DOWN.ordinal());
Game.play(Action2.RIGHT.ordinal());
}
}
如果Action2
定義如下:
package cc.openhome;
public enum Action2 {
STOP, RIGHT, LEFT, UP, DOWN
}
則會有以下執行結果:
播放向右動畫
switch
比對時可以使用Enum
型態,實際上也是利用了Enum
的ordinal()
取得int
值。Enum
的valueOf()
方法,可以傳入字串與Enum
實例,它會傳回對應的列舉實例。例如以下會顯示true
:Action2 action = Enum.valueOf(Action2.class, "UP");
System.out.println(Action2.UP == action);
不過通常會透過
Enum
子類別的valueOf()
方法,其內部就使用了Enum.valueOf()
(可觀察先前反編譯Action
列舉的程式碼)。例如以下會顯示true
:Action2 action = Action2.valueOf("UP");
System.out.println(Action2.UP == action);
Enum
的equals()
與hashCode()
基本上繼承了Object
的行為,但被標示為final
:...略
public final boolean equals(Object other) {
return this==other;
}
public final int hashCode() {
return super.hashCode();
}
...略
由於標示為
final
,所以定義列舉時,不能重新實作equals()
與hashCode()
,這是因為列舉成員,在JVM中只會存在單一實例,Object
定義的equals()
與hashCode()
作為物件相等性比較是適當的定義。