Java中的枚举

Enum在Java 1.5中作为一种新类型被引入,其字段由一组固定的常量组成。例如,我们可以创建代表方向的Java Enum,其固定字段为EAST、WEST、NORTH和SOUTH。

Java Enum

在本教程中,我们将学习如何创建Enum。我们还将探讨在Java中使用enums的好处以及enum类型的特性。我们还将学习使用Java Enum的valueOf、enum valuesEnumSetEnumMap,并附有示例。

Java Enum示例

Java enum关键字用于创建enum类型。让我们看一下Java Enum示例程序。

package com.journaldev.enums;

public enum ThreadStates {
	START,
	RUNNING,
	WAITING,
	DEAD;
}

在上面的例子中,ThreadStates是具有固定常量字段START、RUNNING、WAITING和DEAD的enum。

Java Enum vs 常量

现在让我们看看Java Enum如何优于Java类中的普通常量字段。让我们在Java中创建一个类似的常量类。

package com.journaldev.enums;

public class ThreadStatesConstant {
	public static final int START = 1;
	public static final int WAITING = 2;
	public static final int RUNNING = 3;
	public static final int DEAD = 4;
}

現在讓我們看看在Java程序中如何使用enum和常量:

/**
* This method shows the benefit of using Enum over Constants
*/
private static void benefitsOfEnumOverConstants() {
	//Enum值是固定的
	simpleEnumExample(ThreadStates.START);
	simpleEnumExample(ThreadStates.WAITING);
	simpleEnumExample(ThreadStates.RUNNING);
	simpleEnumExample(ThreadStates.DEAD);
	simpleEnumExample(null);
		
	simpleConstantsExample(1);
	simpleConstantsExample(2);
	simpleConstantsExample(3);
	simpleConstantsExample(4);
	//我們可以傳遞任何int常量
	simpleConstantsExample(5);
}

private static void simpleEnumExample(ThreadStates th) {
	if(th == ThreadStates.START) System.out.println("Thread started");
	else if (th == ThreadStates.WAITING) System.out.println("Thread is waiting");
	else if (th == ThreadStates.RUNNING) System.out.println("Thread is running");
	else System.out.println("Thread is dead");
}
	
private static void simpleConstantsExample(int i) {
	if(i == ThreadStatesConstant.START) System.out.println("Thread started");
	else if (i == ThreadStatesConstant.WAITING) System.out.println("Thread is waiting");
	else if (i == ThreadStatesConstant.RUNNING) System.out.println("Thread is running");
	else System.out.println("Thread is dead");
}

如果我們看上面的例子,使用常量有兩個風險,這些風險可以通過enum來解決。

  1. 我們可以將任何int常量傳遞給simpleConstantsExample方法,但我們只能將固定值傳遞給simpleEnumExample,因此它提供了類型安全性。
  2. 我們可以更改ThreadStatesConstant類中int常量的值,但上面的程序不會拋出任何異常。我們的程序可能不會按預期工作,但如果我們更改enum常量,則會獲得編譯時錯誤,從而消除了任何可能的運行時問題。

Java Enum方法

現在讓我們通過一個例子來看看Java enum的更多功能。

package com.journaldev.enums;

import java.io.Closeable;
import java.io.IOException;

/**
 * This Enum example shows all the things we can do with Enum types
 *
 */
public enum ThreadStatesEnum implements Closeable{
	START(1){
		@Override
		public String toString(){
			return "START implementation. Priority="+getPriority();
		}

		@Override
		public String getDetail() {
			return "START";
		}
	},
	RUNNING(2){
		@Override
		public String getDetail() {
			return "RUNNING";
		}
	},
	WAITING(3){
		@Override
		public String getDetail() {
			return "WAITING";
		}
	},
	DEAD(4){
		@Override
		public String getDetail() {
			return "DEAD";
		}
	};
	
	private int priority;
	
	public abstract String getDetail();
	//Enum構造函數應始終是私有的。
	private ThreadStatesEnum(int i){
		priority = i;
	}
	
	//Enum可以有方法
	public int getPriority(){
		return this.priority;
	}
	
	public void setPriority(int p){
		this.priority = p;
	}
	
	//Enum可以覆蓋函數
	@Override
	public String toString(){
		return "Default ThreadStatesConstructors implementation. Priority="+getPriority();
	}

	@Override
	public void close() throws IOException {
		System.out.println("Close of Enum");
	}
}

Java Enum重要注意事項

以下是Java中枚舉類的一些重要注意事項。

  1. 所有Java枚舉都隱式擴展`java.lang.Enum`類別,該類別擴展Object類別並實現`Serializable`和`Comparable`介面。因此,我們無法在枚舉中擴展任何類別。
  2. 由於enum是關鍵字,我們不能以它結尾作為包名,例如`com.journaldev.enum`不是有效的包名。
  3. 枚舉可以實現介面。就像上面的枚舉示例一樣,它實現了`Closeable`介面。
  4. 枚舉的構造函數始終是私有的。
  5. 我們不能使用new運算符創建枚舉的實例。
  6. 我們可以在Java枚舉中聲明抽象方法,然後所有的枚舉字段必須實現該抽象方法。在上面的例子中,`getDetail()`是抽象方法,所有的枚舉字段都已實現它。
  7. 我們可以在枚舉中定義一個方法,枚舉字段也可以覆蓋它們。例如,`toString()`方法在枚舉中被定義,枚舉字段START已覆蓋它。
  8. Java枚舉字段具有命名空間,我們只能使用類名與枚舉字段,如`ThreadStates.START`。
  9. 枚舉可以在switch語句中使用,我們將在本教程的後部看到它的應用。
  10. 我們可以擴展現有的列舉(enum)而不會破壞任何現有功能。例如,我們可以在ThreadStates列舉(enum)中添加一個新字段NEW,而不會影響任何現有功能。
  11. 由於列舉(enum)字段是常量,Java最佳實踐是將它們寫成大寫字母並用底線表示空格。例如EAST、WEST、EAST_DIRECTION等。
  12. 列舉(enum)常量隱式地是靜態(static)和最終(final)的。
  13. 列舉(enum)常量是最終(final)的,但它的變量仍然可以更改。例如,我們可以使用setPriority()方法來更改列舉(enum)常量的優先級。我們將在下面的示例中看到它的使用。
  14. 由於列舉(enum)常量是最終(final)的,我們可以安全地使用“==”和equals()方法來比較它們。兩者將產生相同的結果。

Java EnumSet,EnumMap,valueOf()

現在我們知道列舉(enum)的大多數特性,讓我們來看一個Java列舉(enum)示例程序。然後我們將學習一些列舉(enum)的更多特性。

package com.journaldev.enums;

import java.io.IOException;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.Set;

public class JavaEnumExamples {

	public static void main(String[] args) throws IOException {
				
		usingEnumMethods();
		
		usingEnumValueOf();
		
		usingEnumValues();
		
		usingEnumInSwitch(ThreadStatesEnum.START);
		usingEnumInSwitch(ThreadStatesEnum.DEAD);
		
		usingEnumMap();
		
		usingEnumSet();
		
	}

	private static void usingEnumSet() {
		EnumSet enumSet = EnumSet.allOf(ThreadStatesEnum.class);
		for(ThreadStatesEnum tsenum : enumSet){
			System.out.println("Using EnumSet, priority = "+tsenum.getPriority());
		}
	}

	private static void usingEnumMap() {
		EnumMap<ThreadStatesEnum, String> enumMap = new EnumMap<ThreadStatesEnum,String>(ThreadStatesEnum.class);
		enumMap.put(ThreadStatesEnum.START, "Thread is started");
		enumMap.put(ThreadStatesEnum.RUNNING, "Thread is running");
		enumMap.put(ThreadStatesEnum.WAITING, "Thread is waiting");
		enumMap.put(ThreadStatesEnum.DEAD, "Thread is dead");
		
		Set keySet = enumMap.keySet();
		for(ThreadStatesEnum key : keySet){
			System.out.println("key="+key.toString()+":: value="+enumMap.get(key));
		}
		
	}

	private static void usingEnumInSwitch(ThreadStatesEnum th) {
		switch (th){
		case START:
			System.out.println("START thread");
			break;
		case WAITING:
			System.out.println("WAITING thread");
			break;
		case RUNNING:
			System.out.println("RUNNING thread");
			break;
		case DEAD:
			System.out.println("DEAD thread");
		}
	}

	private static void usingEnumValues() {
		ThreadStatesEnum[] thArray = ThreadStatesEnum.values();
		
		for(ThreadStatesEnum th : thArray){
			System.out.println(th.toString() + "::priority="+th.getPriority());
		}
	}

	private static void usingEnumValueOf() {
		ThreadStatesEnum th = Enum.valueOf(ThreadStatesEnum.class, "START");
		System.out.println("th priority="+th.getPriority());
	}

	private static void usingEnumMethods() throws IOException {
		ThreadStatesEnum thc = ThreadStatesEnum.DEAD;
		System.out.println("priority is:"+thc.getPriority());
		
		thc = ThreadStatesEnum.DEAD;
		System.out.println("Using overriden method."+thc.toString());
		
		thc = ThreadStatesEnum.START;
		System.out.println("Using overriden method."+thc.toString());
		thc.setPriority(10);
		System.out.println("Enum Constant variable changed priority value="+thc.getPriority());
		thc.close();
	}

}

在解釋列舉(enum)的其他重要特性之前,讓我們看一下上述程序的輸出。

priority is:4
Using overriden method.Default ThreadStatesConstructors implementation. Priority=4
Using overriden method.START implementation. Priority=1
Enum Constant variable changed priority value=10
Close of Enum
th priority=10
START implementation. Priority=10::priority=10
Default ThreadStatesConstructors implementation. Priority=2::priority=2
Default ThreadStatesConstructors implementation. Priority=3::priority=3
Default ThreadStatesConstructors implementation. Priority=4::priority=4
START thread
DEAD thread
key=START:: value=Thread is started
key=RUNNING:: value=Thread is running
key=WAITING:: value=Thread is waiting
key=DEAD:: value=Thread is dead
Using EnumSet, priority = 10
Using EnumSet, priority = 2
Using EnumSet, priority = 3
Using EnumSet, priority = 4

重要要點

  1. usingEnumMethods() 方法顯示了如何創建枚舉對象以及如何使用其方法。它還展示了使用 setPriority(int i) 方法來更改枚舉變量的用法。
  2. usingEnumValueOf() 顯示了使用 java.util.EnumvalueOf(enumType, name) 方法,通過該方法我們可以從字符串創建枚舉對象。如果指定的枚舉類型中沒有具有指定名稱的常量,或者指定的類對象不表示枚舉類型,則會拋出 IllegalArgumentException。如果任何參數為 null,則還會拋出 NullPointerException
  3. usingEnumValues() 方法顯示了使用 values() 方法的用法,該方法返回按宣告順序包含枚舉所有值的數組。請注意,這個方法是由Java編譯器自動為每個枚舉生成的。您在 java.util.Enum 類中找不到 values() 的實現。
  4. usingEnumInSwitch() 方法展示了如何在 switch case 中使用枚舉常量。
  5. usingEnumMap() 方法展示了使用 java.util.EnumMap,它是Java 1.5集合框架中引入的。 EnumMap 是用於枚舉類型鍵的 Map 實現。枚舉映射中的所有鍵必須來自於單個枚舉類型,該類型在創建映射時被指定,明確地或隱式地。我們不能將 null 用作 EnumMap 的鍵,而且 EnumMap 不是同步的。
  6. usingEnumSet() 方法展示了使用 java.util.EnumSet 的示例,這是一個用於 enum 類型的 Set 實現。枚舉集合中的所有元素必須來自於指定的單個枚舉類型,該類型在創建集合時被明確或隱式地指定。EnumSet 不是同步的,且不允許空元素。它還提供了一些有用的方法,如 copyOf(Collection<E> c)of(E first, E... rest)complementOf(EnumSet<E> s)

您可以從我們的 GitHub 存儲庫 中查看所有示例。

參考: Oracle 文檔

Source:
https://www.digitalocean.com/community/tutorials/java-enum