深入瞭解Java枚舉類

深入理解枚舉類

簡介

enum 全稱是 enumeration,是 JDK1.5 引入的新特性,位於Java.lang 包下。
在Java中 enum 是一個關鍵字,被 enum 修飾的類型就是枚舉類。

public enum ColorEnum{
    BLACK,WHITE,GREEN;
}

枚舉的好處

將枚舉類型用作set或map的類型時,專用且高效。(引自枚舉類的介紹)

枚舉的典型應用

如 狀態碼、常量、顏色、類別等

使用規範

  • 枚舉類名帶上Enum後綴,枚舉成員名稱需要全大寫,單詞間用下劃線隔開。
    正例:枚舉名字爲ProcessStatusEnum的成員名稱:SUCCESS / UNKNOWN_REASON
  • 所有的枚舉類型字段必須要有註釋,說明每個數據項的用途

引自阿里巴巴開發手冊

枚舉的本質

枚舉類的聲明

package java.lang;

public abstract class Enum<E extends Enum<E>>
        implements Comparable<E>, Serializable {
            ...
        }

下面我們新建一個枚舉類`ColorEnum``

public enum ColorEnum{
    BLACK,WHITE,GREEN;
}
  1. 執行javac ColorEnum.java 命令,生成ColorEnum.class 文件.
  2. 接着執行javap ColorEnum.class 命令,輸入如下內容:
public final class com.sun.advanced.learnenum.ColorEnum extends java.lang.Enum<com.sun.advanced.learnenum.ColorEnum> {
  public static final com.sun.advanced.learnenum.ColorEnum BLACK;
  public static final com.sun.advanced.learnenum.ColorEnum WHITE;
  public static final com.sun.advanced.learnenum.ColorEnum GREEN;
  public static com.sun.advanced.learnenum.ColorEnum[] values();
  public static com.sun.advanced.learnenum.ColorEnum valueOf(java.lang.String);
  static {};
}

從上面可以看出,枚舉的本質是java.lang.Enum的子類。
儘管enum看起來很像一種新的數據類型,但實際上,enum是一種受限制的類,並且有自己的方法
枚舉類被修飾爲final,所以不能被其他類所繼承。
定義的枚舉值被修飾爲static final,本質上枚舉值就是一種靜態常量

枚舉其實就是特殊的類,域成員均爲常量,且構造方法被默認強制是私有。–阿里巴巴開發手冊

枚舉的方法

  • String name(): 返回此枚舉常量的名稱
  • int ordinal(): 此枚舉常量的序號(它在枚舉聲明中的位置,其中初始常量的序數爲零)。
  • String toString: 返回枚舉常量名
  • boolean equals(Object other): 判斷是否爲同一個對象
  • Class<E> getDeclaringClass(): 返回實例所屬的 enum 類型
  • static <T extends Enum<T>> T valueOf valueOf(Class<T> enumType String name): 返回指定名字、給定類的枚舉常量
  • int compareTo(E other): 如果枚舉常量出現在other之前,則返回用一個負值;如果this==other,則返回0;否則,返回正值。枚舉常量的出現次數在enum中給出

可以使用== 來比較enum實例

enum的基本方法:


public class EnumMethodDemo {

    public static void main(String[] args) {
        System.out.println("******************Print all color**************");
        for (ColorEnum colorEnum : ColorEnum.values()) {
            System.out.println(colorEnum + " ordinal " + colorEnum.ordinal());
        }

        System.out.println("******************Print method**************");

        ColorEnum black = ColorEnum.BLACK;
        System.out.println("black.name(): " + black.name());
        System.out.println("black.getDeclaringClass(): " + black.getDeclaringClass());
        System.out.println("black.hashCode(): " + black.hashCode());
        System.out.println("black.compareTo(ColorEnum.BLACK): " + black.compareTo(ColorEnum.BLACK));
        System.out.println("black.equals(ColorEnum.BLACK): " + black.equals(ColorEnum.BLACK));
        System.out.println("black.equals(ColorEnum.WHITE): " + black.equals(ColorEnum.WHITE));
        System.out.format("black == ColorEnum.WHITE", black == ColorEnum.WHITE);
    }
}

輸出

******************Print all color**************
BLACK ordinal 0
WHITE ordinal 1
GREEN ordinal 2
******************Print method**************
black.name(): BLACK
black.getDeclaringClass(): class com.sun.advanced.learnenum.ColorEnum
black.hashCode(): 1956725890
black.compareTo(ColorEnum.BLACK): 0
black.equals(ColorEnum.BLACK): true
black.equals(ColorEnum.WHITE): false
black == ColorEnum.WHITE
Process finished with exit code 0

枚舉的特性

基本特性

如果枚舉中沒有定義方法,也可以在最後一個實例後面加逗號、分號或什麼都不加

枚舉可以添加方法

枚舉值認爲從0開始的有序數值

枚舉可以添加普通方法、靜態方法、抽象方法、構造方法

Java中不允許使用=爲枚舉常量賦值。而是添加方法來間接實現顯示賦值。

枚舉可以實現接口


public interface INumberEnum {

    int getCode();
    String getDescription();
}


public enum  ErrorCodeEnum implements INumberEnum {

    OK(0,"成功"),

    ERROR_A(100,"錯誤A"),

    ERROR_B(200,"錯誤B");


    private int code;
    private String description;

    ErrorCodeEnum(int number, String description) {
        this.code = number;
        this.description = description;
    }

    public int getCode() {
        return code;
    }

    public String getDescription() {
        return description;
    }
}

枚舉的應用

組織常量

以前,在Java中定義常量都是public static final T A;這樣的形式。有了枚舉類,你可以將有關聯關係的常量組織起來,使代碼更加易讀、安全,並且還可以使用枚舉提供的方法

狀態機

我們經常使用switch語句來寫狀態機。JDK1.7以後,switch已經支持int、charstringenum`類型的參數。這幾種類型的參數比較起來,使用枚舉的switch更具有可讀性

public enum StateEnum {
    GREEN,
    YYELLOW,
    RED;
}


public class StateMachineDemo {

    public static void main(String[] args) {
        System.out.println(getTrafficInstruct(StateEnum.RED));
    }

    public static String getTrafficInstruct(StateEnum singal){
        String instruct = "信號燈故障";
        switch (singal){
            case RED:
                instruct="紅燈停";
                break;
            case GREEN:
                instruct="黃燈請注意";
                break;
            case YYELLOW:
                instruct="綠燈行";
                break;
            default:
                break;
        }
        return instruct;
    }
}

輸出

紅燈停

錯誤碼

枚舉常常用於定義程序錯誤碼。下面使一個簡單示例


public enum HttpStatusEnum {

    OK(200,"成功"),
    NOT_FOUND(404,"資源未找到");

    private int code;
    private String msg;

    HttpStatusEnum(int code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public int getCode() {
        return code;
    }

    public String getMsg() {
        return msg;
    }

    @Override
    public String toString() {
        return super.toString();
    }
}


public class HttpStateDemo {

    public static void main(String[] args) {
        for (HttpStatusEnum value : HttpStatusEnum.values()) {
            System.out.println(value.getCode() +" "+ value.toString());
        }
    }
}

組織枚舉

可以將類型接近的枚舉通過接口或類組織起來來,但是一般用接口的方式進行組織。
原因是:Java接口在編譯時會自動爲enum類型加上public static修飾符;Java類編譯時會自動爲enum類型加上static修飾符,就是說,在類組織enum,如果你不給他修飾爲public,那麼之惡能在本包中進行訪問

在一個接口的內部,創建實現該接口的枚舉,以此將元素進行分組,可以達到將枚舉元素分類組織的目的。舉例來說,假設你想用enum來表示不同類別的食物,同時還希望每個enum元素仍然保持Food類型,那麼可以這樣實現:


public interface Food {

    enum Appetizer implements Food{
        SALAD,SOUP,SPRING_ROLLS;
    }

    enum MainCourse implements Food{
        LASAGNE,BURRITO,PAD_THAI,
        LENTILS,HUMMOUS,VINDALOO;
    }

    enum Dessert implements Food{
        TIRAMISU,GELATO,FRUIT;
    }

    enum Coffee implements Food{
        BLACK_COFFEE,DECAF_COFFEE,ESPRESSO,
        LATTE,CAPPUCCINO,TEA,HERB_TEA;
    }
}

這些枚舉都實現了Food接口,所以可以利用Food對具體實例進行實例化

import static com.sun.advanced.learnenum.Food.*;

public class TypeOfFood {
    public static void main(String[] args) {
        Food food = Appetizer.SALAD;
        food = MainCourse.LASAGNE;
        food = Dessert.GELATO;
        food = Coffee.BLACK_COFFEE;
    }
}

枚舉工具類

Java 中提供了兩個方便操作enum的工具類EnumSetEnumMap

EnumSet

Set是一種集合,只能向其中添加不重複的對象。當然enum也要求成員都是唯一的,所以enum看起來也具有集合的行爲。EnumSet是枚舉類型的高性能Set實現。它要求放入它的枚舉常量必須屬於同一枚舉類型

主要接口:

  • noneOf:創建一個具有指定元素類型的空EnumSet
  • allOf:創建一個指定元素類型幷包含所有枚舉值的EnumSet
  • range: 創建一個包括枚舉值中指定範圍元素的EnumSet
  • complenmentsOf:初始化集合包括指定集合的補集
  • of:創建一個包括參數中所有元素的EnumSet
  • copyOf:創建一個包含參數容器的所有元素的EnumSet
public class EnumSetDemo {

    public static void main(String[] args) {

        EnumSet<HttpStatusEnum> httpStatusEnums = EnumSet.allOf(HttpStatusEnum.class);
        for (HttpStatusEnum value : httpStatusEnums) {
            System.out.println(value.name());
        }
    }
}

EnumMap

EnumMap是一種特殊的Map,它要求其中的key必須來自一個enum。由於enum本身的限制,所以EnumMap在內部可由數組實現。因此EnumMap的速度很快,我們可以放心的使用enum實例在EnumMap中進行查找操作。不過我們只能將enum的實例作爲key來調用put()方法,其他操作與使用一般的Map差不多。


public class EnumMapDemo {

    public static void main(String[] args) {

        EnumMap enumMap = new EnumMap(StateEnum.class);
        enumMap.put(StateEnum.RED,"紅燈");
        enumMap.put(StateEnum.GREEN,"綠燈");
        enumMap.put(StateEnum.YYELLOW,"黃燈");

        for (Object o : enumMap.entrySet()) {
            System.out.println(o);
        }
    }
}

參考資料

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