《On Java 8》第22章 枚舉

關鍵字 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 的狀態機

枚舉類型非常適合用來創建狀態機。一個狀態機可以具有有限個特定狀態,它通常根據輸入,從一個狀態轉移到下一個狀態。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章