瞭解java.lang.Enum類別


有了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類別,可以如下操作:
  • GameDemo.java
package cc.openhome;

public class GameDemo {
public static void main(String[] args) {
Game.play(Action.DOWN.ordinal());
Game.play(Action.RIGHT.ordinal());
}
}

  • Action.java
package cc.openhome;

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

switch比對時可以使用Enum型態,實際上也是利用了Enum的ordinal()取得int值。

Enum的valueOf()方法,可以傳入字串與Enum實例,它會傳回對應的列舉實例。例如以下會顯示true:
Action action = Enum.valueOf(Action.class, "UP");
System.out.println(Action.UP == action);

不過通常會透過Enum子類別的valueOf()方法,其內部就使用了Enum.valueOf()(可觀察先前反編譯Action列舉的程式碼)。例如以下會顯示true:
Action action = Action.valueOf("UP");
System.out.println(Action.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()作為物件相等性比較是適當的定義。