列舉(enum)型別
關鍵字enum 可以將一組具名的值的有限集合建立一種新的型別,而這些具名的值可以作為常規的程式使用。建立enum 時,編譯器會為你生成一個相關的類,這個類繼承自java.lang.Enum 。下面是一個例子演示了一些Enum 提供的一些功能。
package com.jas.enumtest;
enum Animal{CAT,DOG,SHEEP}
public class EnumClass {
public static void main(String[] args) {
for(Animal animal : Animal.values()){
System.out .println(animal + " oridinal:" + animal.ordinal());
System.out.print(animal.compareTo(Animal.DOG) + " ");
System.out.print(animal.equals(Animal.DOG) + " ");
System.out.println(animal == Animal.DOG);
System.out.println(animal.getDeclaringClass());
System.out .println(animal.name());
System.out.println("--------分割線----------");
}
for(String str : "DOG,CAT,SHEEP".split(",")){
Animal animal = Animal.valueOf(Animal.class,str);
System.out.print(animal + " ");
}
}
}
輸出:
CAT oridinal:0
-1 false false
class com.jas.enumtest.Animal
CAT
——–分割線———-
DOG oridinal:1
0 true true
class com.jas.enumtest.Animal
DOG
——–分割線———-
SHEEP oridinal:2
1 false false
class com.jas.enumtest.Animal
SHEEP
——–分割線———-
DOG CAT SHEEP
ordinal() 方法返回一個int 型別的值,這個值是enum 在例項中宣告的順序,從0 開始。可以使用 == 來比較enum 例項,編譯器會自動為你提供equals() 方法與 hashCode() 方法。Enum 類實現了Comparable 介面,所以你可以使用compareTo() 方法。同時Enum 類還實現了Serializable 介面。
getDeclaringClass() 方法返回其所屬的類。
name 方法返回enum 例項宣告時的名字,這與toString 方法返回的值相同。valueOf() 是在Enum 中定義的static 方法,它可以根據給定的名字返回相應的enum 例項,如果不存在給定名字的例項,就會丟擲異常。
覆蓋enum 的方法:
當我們想要覆蓋enum 中的方法時,我們會發現,它就只有一個方法toString(),當我們覆蓋它時與覆蓋一般的類的方法沒有區別。
enum Animal{
CAT,DOG,SHEEP;
@Override
public String toString() {
String id = name();
String lower = id.substring(1).toLowerCase();
return id.charAt(0) + lower;
}
}
public class EnumClass {
public static void main(String[] args) {
for(Animal animal : Animal.values())
System.out.print(animal + " ");
}
}
輸出
Cat Dog Sheep
向enum 中新增方法:
除了不能繼承一個enum 之外,我們基本上可以將enum 看作是一個常規的類。這也意味著enum 擁有自己的建構函式,以及我們可以向enum 中新增方法。它甚至是可以擁有main() 方法。這裡我們通過一個方法返回一個列舉型別的描述不再僅僅是一個toString() 型別的實現。
enum Animal{
// private String description; 這是不允許的
CAT("喵喵喵~"),
DOG("汪汪汪~"),
SHEEP("咩咩咩~");
private String description;
private Animal(String description){
this.description = description;
}
public String getDescription(){
return description;
}
}
public class EnumClass {
public static void main(String[] args) {
for(Animal animal : Animal.values())
System.out.println(animal + " : " + animal.getDescription());
}
}
輸出:
CAT : 喵喵喵~
DOG : 汪汪汪~
SHEEP : 咩咩咩~
需要注意的是:如果我們打算自定義方法,那麼必須在enum 徐磊例項的後面新增一個分號。同時Java 要求你必須先定義enum 例項,就像上面這樣我們打算在定義enum 例項之前定義description 但是卻得到一個編譯器的錯誤提示。enum 中的建構函式與方法與普通的類沒有任何區別,除了有少量的限制enum 可以說就是一個類。所以我們可以使用enum 做許多額外的事。
switch 語句中的 enum:
一般來說,在switch 語句中 只能使用整數值,而列舉天生就具備整數值的次序,並且可以通過ordinal() 方法獲得其在enum 中的順序,因此我們可以在switch 中使用列舉。
enum Singal{GREEN, YELLOW, RED}
public class TrafficLight {
Singal color = Singal.RED;
public void change(){
switch (color){
case RED: color = Singal.GREEN;
break;
case GREEN: color = Singal.YELLOW;
break;
case YELLOW: color = Singal.RED;
break;
}
}
public String toString(){
return "The traffic light is : " + color;
}
public static void main(String[] args) {
TrafficLight light = new TrafficLight();
for (int i = 0; i < 4; i++) {
System.out.println(light.toString());
light.change();
}
}
}
輸出:
The traffic light is : RED
The traffic light is : GREEN
The traffic light is : YELLOW
The traffic light is : RED
values() 的神祕之處:
我們知道編譯器為我們建立的enum 都繼承自Enum 類。但是如果你去檢視Enum 類就會發現,它並沒有values() 方法。但是我們在上面確實使用了這個方法,這是為什麼呢?下面我們使用反射機制來檢視其中的原因。
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Set;
import java.util.TreeSet;
enum Explore{HERE,THERE}
public class Reflection {
public static Set<String> analyze(Class<?> enumClass){
Set<String> set = new TreeSet<>();
System.out.println("----- Analyzing " + enumClass + " -----");
for(Type type : enumClass.getGenericInterfaces())
System.out.println("genericInterface : " + type);
System.out.println("superClass : " + enumClass.getSuperclass());
for(Method method : enumClass.getMethods())
set.add(method.getName());
System.out.println("----- end -----");
System.out.println();
return set;
}
public static void main(String[] args) {
Set<String> exploreMethods = analyze(Explore.class);
Set<String> enumMethods = analyze(Enum.class);
System.out.println("exploreMethods : " + exploreMethods);
System.out.println("enumMethods : " + enumMethods);
System.out.println("exploreMethods.containsAll(enumMethods): " + exploreMethods.containsAll(enumMethods));
System.out.println("exploreMethods.removeAll(enumMethods): " + exploreMethods.removeAll(enumMethods));
System.out.println("exploreMethods : " + exploreMethods);
}
}
—– Analyzing class com.jas.enumtest.Explore —–
superClass : class java.lang.Enum
—– end —–
—– Analyzing class java.lang.Enum —–
genericInterface : java.lang.Comparable
genericInterface : interface java.io.Serializable
superClass : class java.lang.Object
—– end —–
exploreMethods : [compareTo, equals, getClass, getDeclaringClass, hashCode, name, notify, notifyAll, ordinal, toString, valueOf, values, wait]
enumMethods : [compareTo, equals, getClass, getDeclaringClass, hashCode, name, notify, notifyAll, ordinal, toString, valueOf, wait]
exploreMethods.containsAll(enumMethods): true
exploreMethods.removeAll(enumMethods): true
exploreMethods : [values]
答案就是values() 方法是由編譯器新增的static 方法。其實這個過程中還未其添加了valueOf() 方法。但是Enum 中明明已經有了valueOf() 方法,為什麼編譯器還會為其新增這個方法呢?Enum 中的valueOf() 方法需要傳遞進來兩個引數,但是新增的這個方法只需要傳遞進來一個引數。
參考書籍:
《Java 程式設計思想》Bruce Eckel 著 陳昊鵬 譯