瞭解 java.lang.Enum


使用 enum 列舉常數 中使用enum定義過以下的Action列舉型態:

public enum Action {
    STOP, RIGHT, LEFT, UP, DOWN
}

在當時談過,enum定義了特殊的類別,繼承自java.lang.Enum,不過這是由編譯器處理,直接撰寫程式繼承Enum類別會被編譯器拒絕,即便如此,想要瞭解列舉型態如何定義與運用,先瞭解Enum類別是必要的。首先看到Enumclass定義:

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值會分別設定給Enumnameordinal成員,因此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型態,實際上也是利用了Enumordinal()取得int值。

EnumvalueOf()方法,可以傳入字串與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);

Enumequals()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()作為物件相等性比較是適當的定義。