Java列舉類(6.9)
參考《java瘋狂講義》
1. 列舉類入門
java 5 新增enum關鍵字,用以定義列舉類。
它與普通類的區別如下:
- 列舉類可以實現一個或多個介面,使用enum定義的列舉類預設繼承了java.lang.Enum類,而不是預設繼承Object類,因此列舉類不能顯式繼承繼承其他父類。其中java.lang.Enum類實現了java.lang.Serializable和java.lang.Comparable介面。
- 使用enum定義,非抽象的列舉類預設使用final修飾,因此列舉類不能派生子類。
- 列舉類的構造器只能使用private修飾,如果省略構造器的訪問控制符,則預設使用private修飾。
- 列舉類的所有例項必須在列舉類的第一行顯式列出,否則這個類永遠不能產生例項。列出這些例項時,系統預設會加上public static final修飾符,無需程式設計師顯式新增。
- 列舉類預設提供了一個values()方法,該方法可以很方便的遍歷所有列舉值。
public enum SeasonEnum
{
// 在第一行列出4個列舉值
SPRING,SUMMER,FALL,WINTER;
}
如果需要使用列舉類的某個例項,可以使用EnumClass.variable的形式。
public class EnumTest { public void judge(SeasonEnum s) { // switch語句裡的表示式可以時列舉值 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 (SeasonEnum s : SeasonEnum.values()) { System.out.println(s); } // 使用列舉例項時,可通過EnumClass.variable的形式訪問 new EnumTest().judge(SeasonEnum.SPRING); } }
switch的控制表示式可以是任何列舉型別。不僅如此,當switch控制表示式使用列舉型別時,後面case表示式中的值直接使用列舉值的名字,無需新增列舉類作為限定。
所有的列舉類都繼承了java.lang.Enum類,所以所有列舉類都可以直接使用java.lang.Enum類的方法。java.lang.Enum類提供瞭如下幾個常用方法:
- int compareTo(E o):該方法用於指定列舉物件的比較順序,同一個列舉例項只能與相同型別的列舉例項進行比較。若該列舉物件在指定列舉物件之後,返回正整數,之後返回負整數,否則返回0
- String name():返回此列舉型別的名稱,用的不多,更多采用下面的方法,因為其返回更加使用者友好的名稱。
- String toString():返回列舉常量的名稱
- int ordinal():返回列舉值在列舉類中的索引值(就是在宣告中的位置,索引值從0開始)
- public static <T extends Enum>T valueOf(ClassenumType,String name):用於返回指定列舉類中指定名稱的列舉值。名稱必須與在該列舉類中宣告列舉值時所用的識別符號完全匹配,不允許使用額外的空白字元。
2. 列舉類的成員變數,方法和構造器
public enum Gender
{
MALE,FEMALE;
// 定義一個public修飾的例項變數
public String name;
}
public class GenderTest
{
public static void main(String[] args)
{
//通過Enum的valueOf()方法來獲取指定列舉型別的列舉值
Gender g = Enum.valueOf(Gender.class , "FEMALE");
//直接為列舉類的name例項變數賦值
g.name = "女";
//直接訪問列舉值的name例項變數
System.out.println(g + "代表:" + g.name);
}
}
為了保護列舉類的成員變數,列舉類通常應該設計成不可變類,也就是說,它的成員變數值因該不允許被改變,因此建議將列舉類的成員變數用private final修飾。如果將所有成員變數都用final修飾符,那麼就必須在構造器裡為這些成員變數賦初值(在定義時,或者初始化塊中指定初始值也可以,不過比較少見)因此應該為列舉類顯示定義帶參的構造器。
public enum Gender
{
// 此處的列舉值必須呼叫對應的構造器來建立
MALE("男"),FEMALE("女");
private final String name;
// 列舉類的構造器只能使用private修飾
private Gender(String name)
{
this.name = name;
}
public String getName()
{
return this.name;
}
}
一旦為列舉類定義了帶引數的構造器,列出列舉值時就必須對應的傳入引數。也就是說,在列舉類中列出列舉值時,實際上就是呼叫構造器建立列舉類物件,只是這裡無需使用new關鍵字和顯式呼叫構造器,。前面列出列舉值時無需傳入引數,甚至無需括號,僅僅是因為前面的列舉類包含無參的構造器。
3. 實現介面的列舉類
列舉類也可以實現一個或多個介面。與普通類實現一個或多個介面一樣,列舉類實現一個或多個介面時,也需要實現該介面所包含的抽象方法。
public enum Gender implements GenderDesc
{
// 此處的列舉值必須呼叫對應的構造器來建立
MALE("男")//②
// 花括號部分實際上是一個類體部分
{
public void info()
{
System.out.println("這個列舉值代表男性");
}
},
FEMALE("女")//②
{
public void info()
{
System.out.println("這個列舉值代表女性");
}
};
private Gender(String name)
{
this.name = name;
}
public String getName()
{
return this.name;
}
public void info()//①
{
System.out.println(
"這是一個用於定義性別的列舉類");
}
}
如果由列舉類實現接口裡的方法,①則每個列舉值在呼叫該方法時都有相同的行為方式(因為方法體一樣)。如果需要每個列舉值在呼叫該方法時呈現出不同的行為方式,則可以讓每個列舉值分別來實現該方法,每個列舉值提供不同的方法體②
當建立兩個列舉值時,後面緊跟花括號,裡面包含了一個方法的定義。有人可能會有點疑問。其實花括號與前面的匿名內部類語法相似。在這種情況下,當建立一個列舉值時,並不是直接建立一個列舉例項,而是建立Gender的匿名子類的例項。
疑問:列舉類不是用final修飾嗎?怎麼還能派生子類。
並不是所有的列舉類都使用了final修飾,非抽象的列舉類才預設使用final修飾,對於一個抽象列舉類而言——只包含了抽象方法,他就是抽象列舉類,系統會預設使用abstract修飾,而不是final修飾。
4. 包含抽象方法的列舉類
public enum Operation
{
PLUS
{
public double eval(double x , double y)
{
return x + y;
}
},
MINUS
{
public double eval(double x , double y)
{
return x - y;
}
},
TIMES
{
public double eval(double x , double y)
{
return x * y;
}
},
DIVIDE
{
public double eval(double x , double y)
{
return x / y;
}
};
// 為列舉類定義一個抽象方法
// 這個抽象方法由不同的列舉值提供不同的實現
public abstract double eval(double x, double y);
public static void main(String[] args)
{
System.out.println(Operation.PLUS.eval(3, 4));
System.out.println(Operation.MINUS.eval(5, 4));
System.out.println(Operation.TIMES.eval(5, 4));
System.out.println(Operation.DIVIDE.eval(5, 4));
}
}
列舉類裡定義抽象方法時不能用abstract修飾符將列舉類定義成抽象類(因為系統自動會為它新增abstract),但因為列舉值需要顯示建立列舉值,而不是作為父類,所以定義每個列舉值時必須為抽象方法提供實現,否則將出現編譯錯誤。