關鍵字 enum 可以將一組具名的值的有限集合創建爲一種新的類型,而這些具名的值可以作爲常規的程序組件使用。
基本 enum 特性
enum Letter {C, B, A}
public class A {
public static void main(String[] args) {
for (Letter letter : Letter.values()) {
System.out.println(letter + " ordinal: " + letter.ordinal());
}
System.out.println();
System.out.println("A compareTo B: " + Letter.A.compareTo(Letter.B));
System.out.println("B compareTo B: " + Letter.B.compareTo(Letter.B));
System.out.println("C compareTo B: " + Letter.C.compareTo(Letter.B));
System.out.println();
Letter letter = Enum.valueOf(Letter.class, "A");
System.out.println(letter);
letter = Enum.valueOf(Letter.class, "L");
}
}
// 運行結果:
C ordinal: 0
B ordinal: 1
A ordinal: 2
A compareTo B: 1
B compareTo B: 0
C compareTo B: -1
A
Exception in thread "main" java.lang.IllegalArgumentException: No enum constant haha.Letter.L
at java.lang.Enum.valueOf(Enum.java:238)
at haha.A.main(A.java:23)
將靜態類型導入用於 enum
使用 static import 能夠將 enum 實例的標識符帶入當前的命名空間。
// Number.java
public enum Number {
ONE,
TWO,
THREE
}
// Test.java
import static haha.Number.*;
public class Test {
public static void main(String[] args) {
System.out.println(ONE);
}
}
values 方法
創建的 enum 類都繼承自 Enum 類。但 Enum 類並沒有 vlaues() 方法。
// Number.java
public enum Number {
ONE,
TWO
}
// Test.java
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Set;
import java.util.TreeSet;
public class Test {
public static void analyze(Class enumClass) {
System.out.println("analyzing " + enumClass);
System.out.println("interfaces:");
for (Type type : enumClass.getGenericInterfaces()) {
System.out.println(type);
}
System.out.println("superclass:");
System.out.println(enumClass.getSuperclass());
Set<String> set = new TreeSet<>();
for (Method method : enumClass.getMethods())
set.add(method.getName());
System.out.println(set);
System.out.println();
}
public static void main(String[] args) {
analyze(Number.class);
analyze(Enum.class);
}
}
// 運行結果:
analyzing class haha.Number
interfaces:
superclass:
class java.lang.Enum
[compareTo, equals, getClass, getDeclaringClass, hashCode, name, notify, notifyAll, ordinal, toString, valueOf, values, wait]
analyzing class java.lang.Enum
interfaces:
java.lang.Comparable<E>
interface java.io.Serializable
superclass:
class java.lang.Object
[compareTo, equals, getClass, getDeclaringClass, hashCode, name, notify, notifyAll, ordinal, toString, valueOf, wait]
// 反編譯:
javac Number.java
javap -c Number
Compiled from "Number.java"
public final class haha.Number extends java.lang.Enum<haha.Number> {
public static final haha.Number ONE;
public static final haha.Number TWO;
public static haha.Number[] values();
public static haha.Number valueOf(java.lang.String);
static {};
}
values() 是由編譯器添加的 static 方法。
如果將 enum 實例向上轉型爲 Enum,那麼 values() 方法就不能訪問了。
但是可以通過 Class 中的 getEnumConstants() 方法。
Enum e = Number.ONE;
for (Enum x : e.getClass().getEnumConstants())
System.out.println(x);
Supplier(1.8 新特性)
Supplier 用來創建對象,不同於傳統的創建對象語法:new
每次調用 get() 方法時都會調用構造方法創建一個新對象
import java.util.function.Supplier;
public class Test {
Test() {
System.out.println("hello world!");
}
public static void main(String[] args) {
Supplier<Test> supplier = Test::new;
System.out.println("==========");
supplier.get();
supplier.get();
}
}
使用接口組織枚舉
在一個接口的內部,創建實現該接口的枚舉,以此將元素進行分組,可以達到將枚舉元素分類組織的目的。
// Letter.java
public interface Letter {
enum BIG implements Letter {
A, B
}
enum LITTLE implements Letter {
a, b
}
}
// Zimu.java
public enum Zimu {
BIG(Letter.BIG.class),
LITTLE(Letter.LITTLE.class);
private Letter[] letters;
Zimu(Class<? extends Letter> tmp) {
letters = tmp.getEnumConstants();
}
public Letter[] getValue() {
return letters;
}
}
// Test.java
public class Test {
public static void main(String[] args) {
for (Zimu zimu : Zimu.values()) {
for (Letter letter : zimu.getValue()) {
System.out.println(letter);
}
}
}
}
EnumSet
Java SE5 引入 EnumSet,是爲了通過 enum 創建一種替代品,以替代傳統的基於 int 的“位標誌”。
EnumSet<Letter> letters = EnumSet.noneOf(Letter.class);
letters.add(A); // [A]
letters.addAll(EnumSet.of(A, B)); // [A, B]
letters = EnumSet.allOf(Letter.class);
letters.remove(A); // [B, C, D, E]
letters.removeAll(EnumSet.range(A, C)); // [D, E]
letters = EnumSet.complementOf(letters); // [A, B, C]
EnumMap
interface Command {
void action();
}
public class Test {
public static void main(String[] args) {
EnumMap<Letter, Command> enumMap = new EnumMap<Letter, Command>(Letter.class);
enumMap.put(B, () -> System.out.println("this is B"));
enumMap.put(A, () -> System.out.println("this is A"));
for (Map.Entry<Letter, Command> entry : enumMap.entrySet()) {
System.out.println(entry.getKey());
entry.getValue().action();
}
}
}
// 運行結果:
A
this is A
B
this is B
常量特定方法
可以爲 enum 實例編寫方法,從而爲每個 enum 實例賦予各自不同的行爲。
public enum Number {
ONE {
@Override
String getInfo() {
return "1";
}
},
TWO {
@Override
String getInfo() {
return "2";
}
};
abstract String getInfo();
public static void main(String[] args) {
for (Number number : values()) {
System.out.println(number.getInfo());
}
}
}
使用 enum 的職責鏈
在職責鏈設計模式中,程序員以多種不同的方式來解決一個問題,然後將他們鏈接在一起,當一個請求到來時,遍歷這個鏈,直到鏈中的某個解決方案能夠處理該請求。
使用 enum 的狀態機
枚舉類型非常適合用來創建狀態機。一個狀態機可以具有有限個特定狀態,它通常根據輸入,從一個狀態轉移到下一個狀態。