如何使用Java中的Enum類


Java1.5 中出現了枚舉類型。當一個值都在一個固定的范圍內變化,那就可以使用 enum 類型來定義。比如說,一周有七天,一年有四季。

沒有枚舉類的時候,我們用常量來定義一組范圍值的:

public static class Season {
		public static final int SPRING = 1;
    public static final int SUMMER = 2;
    public static final int AUTUMN = 3;
    public static final int WINTER = 4;
}

通過常量定義的方式有這樣幾個缺點:

  1. 類型不安全。如 Season 類所示,程序執行過程中接收的是任意一個 int 類型的值,完全可能傳入一個 1 到 4 之外的值,導致錯誤的出現。

  2. 一致性差。int 枚舉屬於編譯期常量,編譯完成后,代碼中引用的地方會直接將整數值寫入。也就是說,該 Int 枚舉被修改之后,所有引用它的程序都需要重新編譯。

  3. 類型無指意性。Seaon 枚舉值僅僅是一些無任何含義的整數值,調試期間僅僅是一些魔數。

定義一個 enum

《阿里巴巴Java開發手冊(華山版)》中建議我們這樣定義一個 enum 類:

【參考】枚舉類名帶上 Enum 后綴,枚舉成員名稱需要全大寫,單詞間用下划線隔開。

說明:枚舉其實就是特殊的類,域成員均為常量,且構造方法被默認強制是私有。

正例:枚舉名字為 ProcessStatusEnum 的成員名稱:SUCCESS / UNKNOWN_REASON。

【推薦】如果變量值僅在一個固定范圍內變化用 enum 類型來定義。

說明:如果存在名稱之外的延伸屬性應使用 enum 類型,下面正例中的數字就是延伸信息,表示一年中的第幾個季節。

由此,我們可以定義一個季節的枚舉類,如下所示:

public enum SeasonEnum {
    SPRING(1), SUMMER(2), AUTUMN(3), WINTER(4);

    private int seq;

    SeasonEnum(int seq) {
        this.seq = seq;
    }

    public int getSeq() {
        return seq;
    }
}

enum 類

使用 enum 定義的枚舉類默認繼承 java.lang.Enum

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

java.lang.Enum

變量

// enum 實例的名字
private final String name;
// 定義enum實例的順序,從0開始計數
private final int ordinal;

方法

// 返回enum實例的名稱,eg. SeasonEnum.SPRING.name() - output: SPRING
public final String name() { return name;}
// 返回enum實例的順序,eg. SeasonEnum.SPRING.ordinal() - output: 0
public final int ordinal() { return ordinal;}
// enum實例 -> 字符串 eg. SeasonEnum.SPRING.toString() - output: SPRING
public String toString() { return name;}
// 比較是不是一個constant,即兩個enum實例的順序(oridnal)是否相同
public final int compareTo(E o) { ... return self.ordinal - other.ordinal; }
// 比較是不是同一個對象
public final boolean equals(Object other) { return this==other;}
// 返回聲明枚舉常量的類的類對象
public final Class<E> getDeclaringClass(){...}
// 返回指定name的enum實例
public static <T extends Enum<T>> T valueOf(Class<T> enumType,String name) {}

// values()是編譯器產生的一個方法,API中沒有,可以用來遍歷
public static SeasonEnum getSeasonByOrdinal(int seq) {
	for (SeasonEnum season: SeasonEnum.values()) {
		if(season.getSeq() == seq) {
			return season;
		}
	}
	return null;
}

enum 的用法

用法一:定義常量

也就是最開始說的,enum 出現之后,用 enum 來代替常量類型。

public static class Season {
		public static final int SPRING = 1;
    public static final int SUMMER = 2;
    public static final int AUTUMN = 3;
    public static final int WINTER = 4;
}

public enum SeasonEnum {
    SPRING, SUMMER, AUTUMN, WINTER;
}

用法二:switch

Java1.5 推出了 enum,Java1.6 支持在 switch 中使用 enum 類型。


public enum OperationUseSwitch {
	PLUS, MINUS, TIMES, DIVIDE;
 
	double apply(double x, double y) {
		switch (this) {
			case PLUS:
				return x + y;
			case MINUS:
				return x - y;
			case TIMES:
				return x * y;
			case DIVIDE:
				return x / y;
		}
		// 如果this不屬於上面四種操作符,拋出異常
		throw new AssertionError("Unknown operation: " + this);
	}
}

用法三:特定於常量的方法實現

特定於常量的方法實現指的是,在 enum 類中定義一個抽象方法,然后各個枚舉常量需要實現這個方法。這樣做的優點在於,相對於 switch 語句,抽象程度更高,每個 enum 實例都需要實現統一的方法,不會漏下。

public enum Operation {
	PLUS {
		double apply(double x, double y) {
			return x + y;
		}
	},
	MINUS {
		double apply(double x, double y) {
			return x - y;
		}
	},
	TIMES {
		double apply(double x, double y) {
			return x * y;
		}
	},
	DIVIDE {
		double apply(double x, double y) {
			return x / y;
		}
	};
 
	abstract double apply(double x, double y);
}

用法四:實現接口

enum 類都隱式得繼承自 java.lang.Enum,而 Java 只支持單繼承,所有 enum 類不能再繼承其它類,但是可以實現接口。比如用法三中將統一的方法抽象出來,就可以使用接口來實現:

public interface Behavior {
    double apply(double x, double y);
}

public enum  Operation implements Behavior{
    PLUS {
        public double apply(double x, double y) {
            return x + y;
        }
    },
    MINUS {
        public double apply(double x, double y) {
            return x - y;
        }
    },
    TIMES {
        public double apply(double x, double y) {
            return x * y;
        }
    },
    DIVIDE {
        public double apply(double x, double y) {
            return x / y;
        }
    };
}

用法五:覆蓋 Enum 的方法

public class Test {
    public enum Color {
        RED("紅色", 1), GREEN("綠色", 2), BLANK("白色", 3), YELLO("黃色", 4);
        // 成員變量
        private String name;
        private int index;

        // 構造方法
        private Color(String name, int index) {
            this.name = name;
            this.index = index;
        }

        // 覆蓋方法
        @Override
        public String toString() {
            return this.index + "_" + this.name;
        }
    }

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

EnumSet 和 EnumMap

EnumSet 和 EnumMap 是兩個為枚舉而設計的集合。EnumSet保證集合中的元素不重復;EnumMap中的 key 是enum 類型,而 value 則可以是任意類型。

EnumMap

public class Herb {
    public enum Type { ANNUAL, PERENNIAL, BIENNIAL }
 
    private final String name;
    private final Type type;
    
    Herb(String name, Type type) {
        this.name = name;
        this.type = type;
    }
 
    @Override public String toString() {
        return name;
    }
}

public static void main(String[] args) {
    Herb[] garden = { new Herb("Basil", Type.ANNUAL),
            new Herb("Carroway", Type.BIENNIAL),
            new Herb("Dill", Type.ANNUAL),
            new Herb("Lavendar", Type.PERENNIAL),
            new Herb("Parsley", Type.BIENNIAL),
            new Herb("Rosemary", Type.PERENNIAL) };
 
    // Using an EnumMap to associate data with an enum - Page 162
    Map<Herb.Type, Set<Herb>> herbsByType = new EnumMap<Herb.Type, Set<Herb>>(
            Herb.Type.class);
    for (Herb.Type t : Herb.Type.values())
        herbsByType.put(t, new HashSet<Herb>());
    for (Herb h : garden)
        herbsByType.get(h.type).add(h);
    System.out.println(herbsByType);
 
}

EnumSet

EnumSet 是枚舉類型的高性能 Set 實現,它要求放入它的枚舉常量必須屬於同一枚舉類型。EnumSet 提供了許多工廠方法以便於初始化。

方法名稱 描述
allOf(Class element type) 創建一個包含指定枚舉類型中所有枚舉成員的 EnumSet 對象
complementOf(EnumSet s) 創建一個與指定 EnumSet 對象 s 相同的枚舉類型 EnumSet 對象, 並包含所有 s 中未包含的枚舉成員
copyOf(EnumSet s) 創建一個與指定 EnumSet 對象 s 相同的枚舉類型 EnumSet 對象, 並與 s 包含相同的枚舉成員
noneOf(<Class elementType) 創建指定枚舉類型的空 EnumSet 對象
of(E first,e...rest) 創建包含指定枚舉成員的 EnumSet 對象
range(E from ,E to) 創建一個 EnumSet 對象,該對象包含了 from 到 to 之間的所有枚 舉成員

EnumSet 作為 Set 接口實現,它支持對包含的枚舉常量的遍歷。

for(Operation op:EnumSet.range(Operation.PLUS,Operation.MULTIPLY)) {
    doSomeThing(op);
}

參考文獻

W3CSchool-Java枚舉

Java 語言中 Enum 類型的使用介紹

Java 枚舉(enum) 詳解7種常見的用法

Java枚舉 —— 很少被使用,或許是因為真正了解它的人太少了

Java枚舉(enum)詳解:Java聲明枚舉類型、枚舉(enum)類、EnumMap 與 EnumSet


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM