1. 程式人生 > 其它 >Java 列舉類

Java 列舉類

Java 列舉類

列舉是一個被命名的整型常數的集合,用於宣告一組帶識別符號的常數。列舉在曰常生活中很常見,例如一個人的性別只能是“男”或者“女”,一週的星期只能是 7 天中的一個等。類似這種當一個變數有幾種固定可能的取值時,就可以將它定義為列舉型別。

在 JDK 1.5 之前沒有列舉型別,那時候一般用介面常量來替代。而使用 Java 列舉型別 enum 可以更貼近地表示這種常量。

宣告列舉

宣告列舉時必須使用 enum 關鍵字,然後定義列舉的名稱、可訪問性、基礎型別和成員等。列舉宣告的語法如下:

enum-modifiers enum enumname:enum-base {
    enum-body,
}

其中,enum-modifiers 表示列舉的修飾符主要包括 public、private 和 internal;enumname 表示宣告的列舉名稱;enum-base 表示基礎型別;enum-body 表示列舉的成員,它是列舉型別的命名常數。

任意兩個列舉成員不能具有相同的名稱,且它的常數值必須在該列舉的基礎型別的範圍之內,多個列舉成員之間使用逗號分隔。

提示:如果沒有顯式地宣告基礎型別的列舉,那麼意味著它所對應的基礎型別是 int。

例 1

下面程式碼定義了一個表示性別的列舉型別 SexEnum 和一個表示顏色的列舉型別 Color。

public enum SexEnum {
    male,female;
}
public enum Color {
    RED,BLUE,GREEN,BLACK;
}

之後便可以通過列舉型別名直接引用常量,如 SexEnum.male、Color.RED。

使用列舉還可以使 switch 語句的可讀性更強,例如以下示例程式碼:

enum Signal {
    // 定義一個列舉型別    
    GREEN,YELLOW,RED
}

public class TrafficLight {
    Signal color = Signal.RED;
    public void change() {
        switch(color) {
            case RED:
                color = Signal.GREEN;
                break;
            case YELLOW:
                color = Signal.RED;
                break;
            case GREEN:
                color = Signal.YELLOW;
                break;
        }
    }
}

列舉類

Java 中的每一個列舉都繼承自 java.lang.Enum 類。當定義一個列舉型別時,每一個列舉型別成員都可以看作是 Enum 類的例項,這些列舉成員預設都被 final、public, static 修飾,當使用列舉型別成員時,直接使用列舉名稱呼叫成員即可。

所有列舉例項都可以呼叫 Enum 類的方法,常用方法如表 1 所示。

方法名稱 描述
values() 以陣列形式返回列舉型別的所有成員
valueOf() 將普通字串轉換為列舉例項
compareTo() 比較兩個列舉成員在定義時的順序
ordinal() 獲取列舉成員的索引位置

例 2

通過呼叫列舉型別例項的 values( ) 方法可以將列舉的所有成員以陣列形式返回,也可以通過該方法獲取列舉型別的成員。

下面的示例建立一個包含 3 個成員的列舉型別 Signal,然後呼叫 values() 方法輸出這些成員。

enum Signal {
    // 定義一個列舉型別
    GREEN,YELLOW,RED;
}

public static void main(String[] args) {
    for(int i = 0;i < Signal.values().length;i++) {
        System.out.println("列舉成員:"+Signal.values()[i]);    
    }
}

輸出結果如下:

列舉成員:GREEN
列舉成員:YELLOW
列舉成員:RED

例 3

建立一個示例,呼叫valueOf() 方法獲取列舉的一個成員,再呼叫 compareTo() 方法進行比較,並輸出結果。具體實現程式碼如下:

public class TestEnum {
    public enum Sex {
        // 定義一個列舉
        male,female;
    }
    public static void main(String[] args) {
        compare(Sex.valueOf("male"));	// 比較    
    }
    public static void compare(Sex s) {
        for(int i = 0;i < Sex.values().length;i++) {
            System.out.println(s + "與" + Sex.values()[i] + "的比較結果是:" + s.compareTo(Sex.values()[i]));        
        }
    }
}

上述程式碼中使用 Sex.valueOf("male") 取出列舉成員 male 對應的值,再將該值與其他列舉成員進行比較。最終輸出結果如下:

male與male的比較結果是:0
male與female的比較結果是:-1

例 4

通過呼叫列舉型別例項的ordinal() 方法可以獲取一個成員在列舉中的索引位置。下面的示例建立一個包含 3 個成員的列舉型別 Signal,然後呼叫 ordinal() 方法輸出成員及對應索引位置。

具體實現程式碼如下:

public class TestEnum1 {
    enum Signal {
        // 定義一個列舉型別
        GREEN,YELLOW,RED;
    }
    public static void main(String[] args) {
        for(int i = 0;i < Signal.values().length;i++) {
            System.out.println("索引" + Signal.values()[i].ordinal()+",值:" + Signal.values()[i]);      
        }
    }
}

輸出結果如下:

索引0,值:GREEN
索引1,值:YELLOW
索引2,值:RED

為列舉新增方法

Java 為列舉型別提供了一些內建的方法,同時列舉常量也可以有自己的方法。此時要注意必須在列舉例項的最後一個成員後新增分號,而且必須先定義列舉例項。

例 5

下面的程式碼建立了一個列舉型別 WeekDay,而且在該型別中添加了自定義的方法。

enum WeekDay {
    Mon("Monday"),Tue("Tuesday"),Wed("Wednesday"),Thu("Thursday"),Fri("Friday"),Sat("Saturday"),Sun("Sunday");
    // 以上是列舉的成員,必須先定義,而且使用分號結束
    private final String day;
    private WeekDay(String day) {
        this.day = day;
    }
    public static void printDay(int i) {
        switch(i) {
            case 1:
                System.out.println(WeekDay.Mon);
                break;
            case 2:
                System.out.println(WeekDay.Tue);
                break;
            case 3:
                System.out.println(WeekDay.Wed);
                break;
            case 4:
                System.out.println(WeekDay.Thu);
                break;
            case 5:
                System.out.println(WeekDay.Fri);
                break;
            case 6:
                System.out.println(WeekDay.Sat);
                break;
            case 7:
                System.out.println(WeekDay.Sun);
                break;
            default:
                System.out.println("wrong number!");
        }
    }
    public String getDay() {
        return day;
    }
}

上面程式碼建立了 WeekDay 列舉型別,下面遍歷該列舉中的所有成員,並呼叫 printDay() 方法。示例程式碼如下:

public static void main(String[] args) {
    for(WeekDay day : WeekDay.values()) {
        System.out.println(day+"====>" + day.getDay());
    }
    WeekDay.printDay(5);
}

輸出結果如下:

Mon====>Monday
Tue====>Tuesday
Wed====>Wednesday
Thu====>Thursday
Fri====>Friday
Sat====>Saturday
Sun====>Sunday
Fri

Java 中的 enum 還可以跟 Class 類一樣覆蓋基類的方法。下面示例程式碼建立的 Color 列舉型別覆蓋了 toString() 方法。

public class Test {
    public enum Color {
        RED("紅色",1),GREEN("綠色",2),WHITE("白色",3),YELLOW("黃色",4);
        // 成員變數
        private String name;
        private int index;
        // 構造方法
        private Color(String name,int index) {
            this.name = name;
            this.index = index;
        }
        // 覆蓋方法
        @Override
        public String toString() {
            return this.index + "-" + this.name;
        }
    }
    public static void main(String[] args) {
        System.out.println(Color.RED.toString());    // 輸出:1-紅色
    }
}

EnumMap 與 EnumSet

為了更好地支援列舉型別,java.util 中添加了兩個新類:EnumMapEnumSet。使用它們可以更高效地操作列舉型別。

EnumMap 類

EnumMap 是專門為列舉型別量身定做的 Map 實現。雖然使用其他的 Map(如 HashMap)實現也能完成列舉型別例項到值的對映,但是使用 EnumMap 會更加高效。

HashMap 只能接收同一列舉型別的例項作為鍵值,並且由於列舉型別例項的數量相對固定並且有限,所以 EnumMap 使用陣列來存放與列舉型別對應的值,使得 EnumMap 的效率非常高。

例 6

下面是使用 EnumMap 的一個程式碼示例。列舉型別 DataBaseType 裡存放了現在支援的所有資料庫型別。針對不同的資料庫,一些資料庫相關的方法需要返回不一樣的值,例如示例中 getURL() 方法。

// 定義資料庫型別列舉
public enum DataBaseType {
    MYSQUORACLE,DB2,SQLSERVER
}
// 某類中定義的獲取資料庫URL的方法以及EnumMap的宣告
private EnumMap<DataBaseType,String>urls = new EnumMap<DataBaseType,String>(DataBaseType.class);
public DataBaseInfo() {
    urls.put(DataBaseType.DB2,"jdbc:db2://localhost:5000/sample");
    urls.put(DataBaseType.MYSQL,"jdbc:mysql://localhost/mydb");
    urls.put(DataBaseType.ORACLE,"jdbc:oracle:thin:@localhost:1521:sample");
    urls.put(DataBaseType.SQLSERVER,"jdbc:microsoft:sqlserver://sql:1433;Database=mydb");
}
//根據不同的資料庫型別,返回對應的URL
// @param type DataBaseType 列舉類新例項
// @return
public String getURL(DataBaseType type) {
    return this.urls.get(type);
}

在實際使用中,EnumMap 物件 urls 往往是由外部負責整個應用初始化的程式碼來填充的。這裡為了演示方便,類自己做了內容填充。

從本例中可以看出,使用 EnumMap 可以很方便地為列舉型別在不同的環境中繫結到不同的值上。本例子中 getURL 繫結到 URL 上,在其他的程式碼中可能又被繫結到資料庫驅動上去。

EnumSet 類

EnumSet 是列舉型別的高效能 Set 實現,它要求放入它的列舉常量必須屬於同一列舉型別。EnumSet 提供了許多工廠方法以便於初始化,如表 2 所示。

方法名稱 描述
allOf(Class element type) 建立一個包含指定列舉型別中所有列舉成員的 EnumSet 物件
complementOf(EnumSet s) 建立一個與指定 EnumSet 物件 s 相同的列舉型別 EnumSet 物件, 幷包含所有 s 中未包含的列舉成員
copyOf(EnumSet s) 建立一個與指定 EnumSet 物件 s 相同的列舉型別 EnumSet 物件, 並與 s 包含相同的列舉成員
noneOf(<Class elementType) 建立指定列舉型別的空 EnumSet 物件
of(E first,e...rest) 建立包含指定列舉成員的 EnumSet 物件
range(E from ,E to) 建立一個 EnumSet 物件,該物件包含了 from 到 to 之間的所有枚 舉成員

EnumSet 作為 Set 介面實現,它支援對包含的列舉常量的遍歷。

for(Operation op:EnumSet.range(Operation.PLUS,Operation.MULTIPLY)) {
    doSomeThing(op);
}