天天看點

java enum用法_Java 枚舉

java enum用法_Java 枚舉

一、枚舉簡介

某些情況下,類的對象有限且固定,比如季節,有春夏秋冬四個對象,行星類目前隻有8個對象,是以,像這些執行個體固定且有限的類稱為枚舉類。

JDK 1.5

之前沒有枚舉類型,那時候一般用接口常量來替代,如:

public static final int SEASON_SPRING=1;
public static final int SEASON_SUMMER=2;
public static final int SEASON_FALL=3;
public static final int SEASON_WINTER=4;
           

這種方式雖然定義簡單,但是類型不安全,季節實際上是一個int的整數,但是int整數之間可以加減,那麼進行

SEASON_SPRING+SEASON_SUMMER

,這樣的代碼完全正常,但是不合常理,還有列印輸出不明确,當列印季節

SEASON_SPRING

時,實際上列印了數字1。而使用

Java

枚舉的出現可以更恰當地表示該常量。

使用

enum

關鍵詞定義,與

class

interface

地位是一樣的,枚舉類也是一種特殊的類,它一樣可以有自己的成員變量、方法,可以實作一個或者多個接口,也可以定義自己的構造器。一個

Java

源檔案最多隻能定義一個

public

通路權限的枚舉類,且該

Java

源檔案也必須和該枚舉類的類名相同。

但是枚舉終究不是普通的類,與普通的類有着如下的簡單差別:

  • 枚舉類可以實作一個或者多個接口,使用

    enum

    定義的枚舉類預設繼承了

    java.lang.Enum

    類,而不是預設繼承了

    Object

    類,是以枚舉類不能顯式繼承其他父類,其中

    java.lang.Enum

    類實作了

    java.lang.Serializable

    java.lang.Comparable

    兩個接口;
public abstract class Enum<E extends Enum<E>>
        implements Comparable<E>, Serializable {
}
           

既然枚舉類都繼承了

java.lang.Enum

類,是以枚舉類可以直接使用

java.lang.Enum

類中包含的方法,

java.lang.Enum

類有如下幾種方法

int compare(E o)

:該方法用于與指定對象比較順序,同一個枚舉執行個體隻能與相同類型的枚舉執行個體進行比較。如果該枚舉對象位于指定枚舉對象之後,則傳回正整數;如果該枚舉對象位于指定枚舉對象之前,則傳回負整數;否則傳回0;

String name()

:傳回此枚舉執行個體的名稱,這個名稱就是定義枚舉類時列出所有的枚舉值之一。與此方法相比,大多數程式員優先考慮

toString()

方法,因為

toString()

方法傳回更加使用者友好的名稱

int ordinal()

:傳回枚舉值在枚舉類中的索引值(就是枚舉值在枚舉聲明中的位置,第一個枚舉值得索引值為0);

String toString()

:傳回枚舉常量的名稱,與name方法相似,但是

toString()

方法更加常用;

public static<T extends Enum<T>>T valueOf(Class<T>enumType,String name)

:這是一個靜态方法,用于傳回指定枚舉類中指定名稱的枚舉值,名稱必須與該枚舉類中聲明枚舉值時所用的辨別符完全比對,不允許使用額外的空白字元。

當程式員使用

System.out.println(s)

語句來列印枚舉值時,實際上輸出的是該枚舉值的

toString()

方法,也就是輸出該枚舉值的名字。

  • 使用

    enum

    定義、非抽象的枚舉類預設會使用final修飾,是以枚舉類不能派生子類(對于抽象的枚舉類而言,系統會預設使用

    abstract

    修飾,而不是

    final

    修飾);
  • 枚舉類的構造器隻能用

    private

    通路控制符,如果省略了構造器的通路控制符,預設為

    private

    ;如果強制指定通路控制符,則隻能指定

    private

    修飾符;
  • 枚舉類的所有執行個體必須在枚舉類第一行顯式列出,否則這個枚舉類不能産生執行個體,列出這些執行個體時,系統自動加上

    public static final

    修飾,無需程式員顯式添加;

    枚舉類提供了一個

    value()

    方法,該方法可以很友善地周遊所有的枚舉值:
public enum Season {
    //列出枚舉的4個執行個體
    SPRING,SUMMER,FALL,WINTER;
}
​
public class TestEnum {
    public void judge(Season s){
        switch (s){
            case SPRING:
                System.out.println("春暖花開,正好踏春!");
                break;
            case SUMMER:
                System.out.println("夏日炎炎,适合遊泳!");
                break;
            case FALL:
                System.out.println("秋高氣爽,進補及時!");
                break;
            case WINTER:
                System.out.println("冬日雪飄,圍爐賞雪!");
                break;
        }
    }
    public static void main(String[]args){
        //枚舉類預設有一個values()方法,傳回該枚舉類的所有執行個體
        for(Season s:Season.values()){
            System.out.println(s);
        }
        //使用枚舉執行個體時,可以通過EnumClass.variable形式來通路
        new TestEnum().judge(Season.SPRING);
    }
}
​
運作結果:
    SPRING
    SUMMER
    FALL
    WINTER
    春暖花開,正好踏春!
​
           

上述程式測試了

Season

枚舉類的用法,該類通過了

values()

方法傳回了

Season

枚舉類的所有執行個體,并通過循環疊代輸出了枚舉類的所有的執行個體

不僅如此,上面程式的switch表達式中還使用了

Season

對象作為表達式,這是

JDK1.5

增加枚舉後對于

switch

的擴充:

switch

的控制表達式可以是任何枚舉類型,當

switch

控制表達式使用枚舉類型時,後面的

case

表達式中的值直接使用枚舉值的名字,無需添加枚舉類作為限定。

二、枚舉的成員變量、方法和構造器

枚舉類也是一種類,知識它是比較特殊的類,是以它一樣可以定義成員變量、方法和構造器。下面以定義一個

People

枚舉類,該枚舉類裡包含了一個

name

執行個體變量。

public enum People {
    MALE,FEMALE;
    //定義一個String類型的成員變量
    public String name;
}
​
public class TestPeople {
    public static void main(String[]args){
        People p=People.valueOf(People.class,"MALE");
        //直接給枚舉的成員變量指派
        p.name="女";
        //通路枚舉的name執行個體變量
        System.out.println(p+"表示:"+p.name);
    }
}
​
運作結果:
    MALE表示:女
           

并不能随意通過

new

來建立枚舉類的對象,

Java

應該把所有類設計成良好封裝的類,是以不應該直接通路

People

類的

name

成員變量,而是應該通過方法來控制對

name

的通路,否則可能很混亂的情形,比如将

p.name="男"

,就會表示

FEMALE

代表男的局面,是以可以用改進的

People

類設計

public enum People {
    MALE,FEMALE;
    //定義一個String類型的成員變量
    private String name;
    public void setName(String name){
        switch(this){
            case MALE:
                if(name.equals("男")){
                    this.name=name;
                }else{
                    System.out.println("參數錯誤!");
                    return;
                }
                break;
            case FEMALE:
                if(name.equals("女")){
                    this.name=name;
                }else{
                    System.out.println("參數錯誤!");
                    return;
                }
                break;
        }
    }
    public String getName(){
        return this.name;
    }
}
​
public class TestPeople {
    public static void main(String[]args){
        People p=People.valueOf(People.class,"MALE");
        p.setName("女");
        System.out.println(p+"表示:"+p.getName());
        p.setName("男");
        System.out.println(p+"表示:"+p.getName());
    }
}
​
運作結果:
    參數錯誤!
    MALE表示:null
    MALE表示:男
           

上述代碼通過

get

set

方法将

FEMALE

枚舉值得

name

變量設定為“男”,系統設定會提示錯誤資訊,實際上這種做法還是不夠好,枚舉類通常應該設計成不可變的類,也就是說成員變量值不應該允許改變,這樣會更加安全,代碼也會更加簡潔,是以建議将枚舉類的成員變量都使用

private final

修飾。

如果将所有的成員變量都使用了

final

修飾符來修飾,所有必須在構造器裡為這些成員變量指定初始值(或者在定義成員變量時指定預設值,或者在初始化塊中指定初始值,但是這兩種情況并不常見),是以應該為枚舉類顯式定義帶參數的構造器,一旦為枚舉類顯式定義了帶有參數的構造器,列出枚舉值得時候就必須對應地傳入參數。

public enum People {
    //此處必須調用對應的枚舉構造器來建立
    MALE("男"),FEMALE("女");
    private final String name;
    //枚舉類的構造器必須用private修飾
    private People(String name){
        this.name=name;
    }
    public String getName(){
        return this.name;
    }
}
           

從上面的程式中可以看出,當為

People

枚舉類建立一個

People(String name)

構造器之後,列出枚舉值就應該采用

MALE("男"),FEMALE("女");

來完成,也就是說,在枚舉類彙總列出枚舉值時,實際上就是調用了構造器建立枚舉類的對象,隻是這裡無須使用

new

關鍵詞,也無須顯式調用構造器,前面列出枚舉值時無須傳入參數,甚至無須使用括号,僅僅是因為前面的枚舉類包含無參數的構造器

上面一行代碼等同于:

public static final People MALE=new People("男");
public static final People FEMALE=new People("女");
           

三、實作接口的枚舉類

枚舉類也可以實作一個或者多個接口,與普通類實作一個或多個接口完全一樣,枚舉類實作一個或者多個接口時,也可以實作該接口所包含的方法,

interface GenderDesc{
     void info();
}
public enum Gender implements GenderDesc {
    MALE("男"),FEMALE("女");
    //實作接口方法
    public void info(){
        System.out.println("這是一個用于定義性别的枚舉類");
    }
    private final String name;
    private Gender(String name){
        this.name = name;
    }
​
    public String getName(){
        return this.name;
    }
}
           

如果由枚舉類來實作接口的方法,則每個枚舉值在調用該方法時都會有同樣的行為方式(因為方法體完全一樣),如果需要每個枚舉值在調用該方法時呈現不同的行為方式,則可以讓每個枚舉值分别來實作該方法,每個枚舉值提供不同的實作方式,進而讓不同的枚舉值調用該方法時具有不同的行為方式,在下面的

Genden

枚舉類中,不同的枚舉值對于

info()

方法的實作各不相同

interface GenderDesc{
     void info();
}
public enum Gender implements GenderDesc {
    MALE("男"){
        @Override
        public void info() {
            System.out.println("male info");
        }
    },
    FEMALE("女"){
        @Override
        public void info() {
            System.out.println("female info");
        }
    }; 
    private final String name;
    
    private Gender(String name){
        this.name = name;
    }
    public String getName(){
        return this.name;
    }
}
​
           

上面這種方式,

{}

相當于建立

Gender

的匿名子類的執行個體,

MALE

FEMALE

實際上是

Gender

匿名子類的執行個體,而不是

Gender

類的執行個體,當調用了

MALE

FEMALE

兩個枚舉值的方法時,就會看到兩個枚舉值的方法表現不同的行為方式。

java enum用法_Java 枚舉

四、包含抽象方法的枚舉類

假設有一個

Operation

枚舉類,它的4個枚舉值

PLUS,MINUS,TIMES,DIVIDE

分别代表加減乘除4種運算,該枚舉類需要定義一個

eval()

方法來實運算。

綜上所述,可以考慮為

Operation

枚舉類定義一個

eval()

抽象方法,然後讓4個枚舉值分别為

eval()

提供不同的實作:

public enum  Operation {
    PLUS{
        @Override
        public int eval(int a, int b) {
            return a + b;
        }
    },
    MINUS{
        @Override
        public int eval(int a, int b) {
            return a - b;
        }
    },
    TIMES{
        @Override
        public int eval(int a,int b){
            return a*b;
        }
    },
    DIVIDE{
        @Override
        public int eval(int a,int b){
            return a/b;
        }
    };
    //定義一個抽象方法,
    //每個枚舉值都提供不同的實作
    public abstract int eval(int a, int b);
​
    public static void main(String[] args){
        System.out.println(Operation.PLUS.eval(10, 2));
        System.out.println(Operation.MINUS.eval(10, 2));
        System.out.println(Operation.TIMES.eval(10, 2));
        System.out.println(Operation.DIVIDE.eval(10, 2));
    }
}
​
運作結果:
    12
    8
    20
    5
           

它的4個匿名内部子類分别對應一個

class

檔案,枚舉類裡定義抽象方法時不能使用

abstract

關鍵詞将枚舉定義抽象類(因為系統自動會為它添加

abstract

關鍵詞),但因為枚舉類需要顯式建立枚舉值,而不是作為父類,是以定義每個枚舉值時必須為抽象方法提供實作,否則将出現編譯錯誤。

枚舉集合

java.util.EnumSet

java.util.EnumMap

是兩個枚舉集合。

EnumSet

保證集合中的元素不重複;

EnumMap

中的

key

enum

類型,而

value

則可以是任意類型。

//定義資料庫類型枚舉
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

是枚舉類型的高性能

Set

實作,它要求放入它的枚舉常量必須屬于同一枚舉類型。

EnumSet

提供了許多工廠方法以便于初始化,如圖所示:

java enum用法_Java 枚舉

EnumSet

作為

Set

接口實作,它支援對包含的枚舉常量的周遊:

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

繼續閱讀