枚舉類,java枚舉類
這種執行個體有限而且固定的類,在Java裡稱為枚舉類。例如:季節類,它只有4個對象,還有行星類目前只有8個對象。
5.9.1 手動實現枚舉類
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;
存在幾個問題:(簡單表述)
1. 類型不安全:實際每個季節是int整數
2. 沒有命名空間:
3. 列印輸出的意義不明確:1很難猜測出代表春天。
5.9.2 枚舉類入門
Java5新增一個enum關鍵字(和class、interface關鍵字差不多),用以定義枚舉類。
枚舉類是一種特殊的類,它一樣有自己的成員變數、方法,可以實現一個或者多個介面,也可以定義自己的構造器。
但是枚舉類還是和普通類有簡單區別:
- 枚舉類可以實現一個或多個介面,使用enum定義的枚舉類預設繼承了java.lang.Enum類,而不是預設繼承Object類,因此枚舉類不能顯式繼承其他父類。其中java.lang.Enum類實現了java.lang.Serializable和java.lang.Comparable兩個介面。
- 使用Enum定義,非抽象的枚舉類預設會使用final修飾,所以不能派生子類
- 枚舉類的構造器只能使用private,如果省略了構造器的存取控制符,則預設使用private,
- 枚舉類的所有執行個體必須在枚舉類的第一行顯式列出,否則這個枚舉類永遠都不能產生執行個體,列出這些執行個體時,系統會自動添加public staic final修飾,無須顯式添加
枚舉類預設提供了一個values()方法,該方法可以很方便地遍曆所有的枚舉類
package code;public enum SeasonEnum{ //在第一行列出4個枚舉類執行個體 SPRING,SUMMER,FALL,WINTER;}
EnumTest.java
package code;public class EnumTest{ public void judge(SeasonEnum e){ //switch語句裡的運算式可以是枚舉類 switch(e){ 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(SeasonEnum s:SeasonEnum.values()){ System.out.println(s); } //使用枚舉類時,可通過EnumClass.variable形式來訪問 new EnumTest().judge(SeasonEnum.SPRING); }}
java.lang.Enum類中提供了如下的幾個方法,所有枚舉類可以直接使用這些方法:
- int compareTo(E o ):該方法用於與指定枚舉對象比較順序,同一個枚舉執行個體只能與相同類型的枚舉執行個體進行比較,如果該枚舉對象位於指定枚舉對象之後,則返回正整數,如果該枚舉對象位於指定枚舉對象之前,則返回負整數,否則返回零。
- String name():返回此枚舉執行個體的名稱,,這個名稱就是定義枚舉類時列出的所有 枚舉類之一,但是大多數情況下使用toString()方法
- int ordinal():返回枚舉值在枚舉類中的索引值
- String toString() :返回枚舉常量的名稱,與name方法相似,toString方法更常用
- public static
5.9.3 枚舉類的成員變數、方法和構造器
Gender.java
package code;public enum Gender{ MALE,FEMALE; public String name;}
GenderTest.java
package code;public class GenderTest{ public static void main(String [] args){ Gender g = Enum.valueOf(Gender.class,"FEMALE"); g.name = "女"; System.out.println(g + "代表:" + g.name); }}
java應該把所有類設計成良好封裝的類,所以不應該允許直接存取Gender類的name成員變數,而應該通過方法來控制對name的訪問。
接下來的代碼是改進後的設計
Gender.java
package code;public enum Gender{ MALE,FEMALE; private String name; public void setName(String name){ switch(this){ case MALE: if(name.equals("男")){ this.name = name; }else{ System.out.println("參數錯誤"); } break; case FEMALE: if(name.equals("女")){ this.name = name; }else{ System.out.println("參數錯誤"); } break; } } public String getName(){ return this.name; }}
GenderTest.java
package code;public class GenderTest{ public static void main(String [] args){ Gender g = Enum.valueOf(Gender.class,"FEMALE"); g.setName("女"); System.out.println(g + "代表:" + g.name()); g.setName("男"); System.out.println(g + "代表:" + g.getName()); }}
FEMALE代表:FEMALE
參數錯誤
FEMALE代表:女
實際上這種做法依然不好,枚舉類通常應該設計成不可變類,也就是說,它的成員變數值不應該允許改變,這樣會更安全,而且代碼更加簡潔。因此建議將枚舉類的成員變數都使用private final修飾。
如果將所有的成員變數都使用了final修飾符來修飾,所以必須在構造器裡為這些成員變數指定初始值(或者在定義成員變數時指定預設值,或者在初始化塊中指定初始值,但是這兩種情況不常見),因此應該為枚舉類顯式定義帶參數的構造器。
一旦為枚舉類顯式定義了帶參數的構造器,列出枚舉值時就必須對應地傳入參數。
package code;public enum Gender{ MALE("男"),FEMALE("女"); private final String name; private Gender(String name){ this.name = name; } public String getName(){ return this.name; }
當為Gender枚舉類建立了一個Gender(String name)構造器之後,列出枚舉類就是從MALE(“男”),FEMALE(“女”);這句程式碼完成,也就是說在枚舉類中列出枚舉類時,實際上就是調用構造器建立枚舉類對象。只是這裡無須使用new關鍵字,無須顯式調用構造器。
等同於以下兩句代碼
public static final Gender MALE = new Gender("男");public static final Gender FEMALE = new Gender("女");
5.9.4 實現介面的枚舉類
枚舉類和普通類一樣可以實現一個或多個介面
package code;public enum Gender2 implements GenderDesc{ MALE("男"),FEMALE("女"); private final String name; private Gender(String name){ this.name = name; } public String getName(){ return this.name; } public void info(){ System.out.println("這是一個用於定義性別的枚舉類"); }}
枚舉類實現介面和普通類實現介面完全一樣。
如果由枚舉類來實現介面裡的方法,則每個枚舉值在調用該方法時都有相同的行為方式,(因為方法體完全一樣)如果需要每個枚舉值在調用該方法時呈現不同的行為方式,則可以讓每個枚舉值分別實現該方法,每個枚舉值提供不同的實現方式,從而讓不同的枚舉值調用該方法時具有不同的行為方式。
樣本如下:
package code;public enum Gender2 implements GenderDesc{ MALE("男"){ public void info(){ System.out.println("這個枚舉值代表男性"); } },FEMALE("女"){ public void info(){ System.out.println("這個枚舉值代表女性"); } }; private final String name; private Gender2(String name){ this.name = name; } public String getName(){ return this.name; } /*public void info(){ System.out.println("這是一個用於定義性別的枚舉類"); }*/}
上述代碼中MALE和FEMALE後面的花括弧部分實際上是一個類體部分,類似匿名內部類。在這種情況下,當建立MALE,FEMALE枚舉值時,並不是直接建立Gender枚舉類的執行個體,而是相當於建立了Gender的匿名子類的執行個體。所以括弧內的只是枚舉類的匿名內部子類。
並不是所有的枚舉類都使用了final修飾,非抽象的枚舉類才預設使用final修飾,對於一個抽象的枚舉類,只要它包含抽象方法,他就是抽象枚舉類,系統會預設使用abstract修飾。
5.9.5 包含抽象方法的枚舉類
package code;public enum Operation{ PLUS{ public double eval(double a ,double b){ return (a+b); } }, MINUS{ public double eval(double a ,double b){ return (a-b); } }, TIMES{ public double eval(double a ,double b){ return (a*b); } }, DIVIDE{ public double eval(double a ,double b){ return (a/b); } }; public abstract double eval(double a,double b); public static void main(String [] args){ System.out.println(Operation.PLUS.eval(3,4)); System.out.println(Operation.MINUS.eval(3,4)); System.out.println(Operation.TIMES.eval(3,4)); System.out.println(Operation.DIVIDE.eval(3,4)); }}
7.0
-1.0
12.0
0.75
枚舉類裡定義抽象方法的時候不能使用abstract關鍵字將枚舉類定義成抽象類別(因為系統自動會為它添加abstract關鍵字),但因為枚舉類需要顯式建立枚舉類,而不是作為父類,所以定義每個枚舉值時必須為抽象方法提供實現,否則出錯。