# Java 枚舉(`enum`)
> 原文: [https://javabeginnerstutorial.com/core-java-tutorial/java-enum-enumerations/](https://javabeginnerstutorial.com/core-java-tutorial/java-enum-enumerations/)
在本文中,我將介紹 Java 枚舉,這是在應用中定義和使用常量的最優雅的方式。
這是每個人都知道的基本功能,但還有一些您可能不知道的功能。
## 為什么要使用枚舉?
好吧,您在 Java 代碼中使用了枚舉。 如果您不這樣做,那么您做錯了什么,或者擁有非常簡單的應用而沒有太多復雜功能。
無論如何,讓我們看一下基礎知識。 例如,您想要一個使用工作日的類。 您可以這樣定義它:
```java
public class Schedule {
private ??? workday;
}
```
要存儲工作日,我們創建一個工具類來存儲工作日的常量:
```java
public class Workdays {
public static final String MONDAY = "Monday";
public static final String TUESDAY = "Tuesday";
public static final String WEDNESDAY = "Wednesday";
public static final String THURSDAY = "Thursday";
public static final String FRIDAY = "Friday";
}
```
現在,`Schedule`類將如下所示:
```java
public class Schedule {
// Workdays.MONDAY, Workdays.TUESDAY
// Workdays.WEDNESDAY, Workdays.THURSDAY
// Workdays.FRIDAY
private String workday;
}
```
我想您已經看到了這種方法的缺點:即使`Workdays`類中未定義工作日,也可以輕松地將工作日設置為“星期六”或“星期日”。 此解決方案不是值安全的。 這不是我們想要實現的。
為了解決這個問題,我們將`Workdays`類轉換為一個枚舉:
```java
public enum Workday {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY
}
```
所需的打字次數更少,現在該解決方案是值安全的。 我們只需要調整`Schredule`類:
```java
public class Schedule {
private Workday workday;
public Workday getWorkday() {
return this.workday;
}
public void setWorkday(Workday workday) {
this.workday = workday;
}
}
```
## 條件和枚舉
枚舉非常適合條件表達式(`if`語句或`switch`塊)。 關于枚舉的好處是它們是常量值,因此您可以將它們與`==`運算符進行比較,該運算符比使用`equals()`更優雅-并且避免了可能的`NullPointerExceptions`。 順便說一句:如果您查看`Enum`類`equals()`的實現,您將看到以下內容:
```java
public final boolean equals(Object other) {
return this==other;
}
```
因此,請自由使用`==`,因為這是`equals()`的默認實現。
讓我們看一下如何使用它們的示例:
```java
if(currentSchedule.getWorkday() == Workday.FRIDAY) {
System.out.println("Hurray! Tomorrow is weekend!");
}
```
或在`switch`塊中:
```java
switch(currentSchedule.getWorkday()) {
case MONDAY:
case TUESDAY:
case WEDNESDAY:
case THURSDAY:
System.out.println("Working and working and...");
break;
case FRIDAY:
System.out.println("Hurray! Tomorrow is weekend!");
break;
default:
System.out.println("Unknown day -.-");
}
```
## 迭代枚舉
迭代枚舉非常簡單。 枚舉類定義了一個稱為`values()`的方法,該方法返回給定枚舉的值。 最好是看一個示例:
```java
for(Workday w : Workday.values()) {
System.out.println(w.name());
}
```
上面的示例將產生以下輸出:
```java
MONDAY
TUESDAY
WEDNESDAY
THURSDAY
FRIDAY
```
如您所見,`values()`的順序與枚舉本身中定義的順序相同。 因此,Java 不會進行任何魔術排序。
## 枚舉字段
有時,您只想將枚舉打印到控制臺(或以某種 UI 形式顯示)。 在上面的示例(工作日)中,您可以簡單地使用枚舉的名稱,盡管有時“`TUESDAY`”似乎很喊,而“`Tuesday`”更可取。 在這種情況下,您可以添加和使用`Enum`對象的自定義字段:
```java
public enum Workday {
MONDAY("Monday"),
TUESDAY("Tuesday"),
WEDNESDAY("Wednesday"),
THURSDAY("Thursday"),
FRIDAY("Friday");
private final String representation;
private Workday(String representation) {
this.representation = representation;
}
}
```
如您在上面的示例中所看到的,枚舉獲取一個私有字段,在這種情況下稱為表示形式。 該字段是最終字段,因此以后無法更改,這意味著必須在枚舉構造期間初始化此屬性。 這是通過構造器完成的,并且提供了構造器參數以及枚舉定義。
您可以根據需要在枚舉中擁有任意數量的屬性/字段,但是我建議您將這個數量保持在較低的水平,因為具有 15 個額外屬性的枚舉確實很奇怪。 在這種情況下,我將考慮使用一個類和/或多個枚舉來保存相同的信息。
## 枚舉方法
枚舉字段很好,但是如何訪問該字段? 我告訴過您,新的表示形式是表示該枚舉的值,但是當前我們無法在枚舉本身之外訪問該屬性。
除此之外,還有一個基本方法可用于所有枚舉:`name()`以字符串形式返回當前值的名稱。 這意味著在基本情況下,您可以使用此方法顯示枚舉的值(例如,在 UI 或日志條目中)。 **當然**也存在`toString()`函數,但是有時開發人員會覆蓋它,以使其對程序員更友好(或用戶友好?)顯示。 作為最佳實踐,如果要顯示枚舉的名稱,建議您使用`name()`方法而不是`toString()`。
要從上面更改表示示例(當我們遍歷`values()`時),只需編寫一個函數,該函數將返回新的變量表示并在迭代中使用它:
```java
public enum Workday {
MONDAY("Monday"),
TUESDAY("Tuesday"),
WEDNESDAY("Wednesday"),
THURSDAY("Thursday"),
FRIDAY("Friday");
private final String representation;
private Workday(String representation) {
this.representation = representation;
}
public String getRepresentation() {
return this.representation;
}
}
```
現在更新迭代:
```java
for(Workday w : Workday.values()) {
System.out.println(w.getRepresentation());
}
```
現在,結果如下:
```java
Monday
Tuesday
Wednesday
Thursday
Friday
```
但這不是唯一可以實現枚舉方法的用法。 在下一節中,我們將看到如何將`Enum`值映射到`String`并返回。
## 實現接口
關于枚舉鮮為人知的一件事是它們可以實現接口。 這意味著,如果您需要不同枚舉所需要的通用功能,則可以使用一個接口定義它,而枚舉必須在該接口中實現方法。
一種這樣的用例是使用 JPA 將枚舉值存儲在數據庫中-盡管不是按名稱或順序(可通過`@Enumeration`和`EnumType`獲得),而是通過短代碼。
在這種情況下,您可以創建一個接口,其中包含將枚舉轉換為數據庫表示形式并將枚舉轉換回的方法。
```java
public interface DatabaseEnum<T extends Enum<T>> {
/**
* Converts the database representation back to the enumeration value
*/
T fromDatabase(String representation);
/**
* Converts the enum value to the database representation
*/
String toDatabaseString();
}
```
該接口使用泛型(由`T`類型表示),并在`String`和枚舉類型之間進行轉換。
要實現此接口,請擴展“工作日”示例:
```java
public enum Workday implements DatabaseEnum<Workday>{
MONDAY("Monday"),
TUESDAY("Tuesday"),
WEDNESDAY("Wednesday"),
THURSDAY("Thursday"),
FRIDAY("Friday");
private final String representation;
private Workday(String representation) {
this.representation = representation;
}
public String getRepresentation() {
return this.representation;
}
@Override
public String toDatabaseString() {
return this.representation;
}
@Override
public Workday fromDatabase(String representation) {
for(Workday wd: Workday.values()) {
if(wd.representation.equals(representation)) {
return wd;
}
}
return null;
}
}
```
## 使用枚舉而不是布爾值
有時,您最終得到一個采用布爾表達式的方法(或者您僅獲得一個告訴您使用布爾值的項目說明,但感覺很癢)。 在這種情況下,可以隨意引入一個新的枚舉并使用正確的值而不是布爾值。
例如,一旦我有了一個規范,告訴我必須創建一個帶有一些參數的方法和一個布爾值,稱為“`rightHandSide`”。 實際上,“`rightHandSide`”的默認值為`false`。 首先,我按照規范中的說明實現了該方法,但是這對我來說并不舒服,我認為這是擺脫這種冷酷感覺的另一種方法:
```java
public void someMethod(String someParameter, boolean rightHandSide) {
if(!rightHandSide) {
// do something
}
}
```
好吧,這似乎并不壞,但是如何擴展功能呢? 如果一方不重要怎么辦? 在這種情況下,可以使用其他布爾參數擴展該方法,但從長遠來看,它不會成功。 而且因為“`rightHandSide`”的默認值為`false`,所以我需要創建一個未提供參數的調用,并且使用`false`調用默認方法。
因此,我引入了一個名為`Side`的新枚舉,并替換了布爾參數:
```java
public enum Side {
RIGHT_HAND,
LEFT_HAND
}
public void someMethod(String someParameter, Side side) {
if(side == Side.RIGHT_HAND) {
// do something
}
}
```
現在感覺好多了,以后可以將該方法擴展到更多情況。
## 總結
如您所見,枚舉非常適合替換常量-有時也可以使用布爾方法參數。
- JavaBeginnersTutorial 中文系列教程
- Java 教程
- Java 教程 – 入門
- Java 的歷史
- Java 基礎知識:Java 入門
- jdk vs jre vs jvm
- public static void main(string args[])說明
- 面向初學者的 Java 類和對象教程
- Java 構造器
- 使用 Eclipse 編寫 Hello World 程序
- 執行順序
- Java 中的訪問修飾符
- Java 中的非訪問修飾符
- Java 中的數據類型
- Java 中的算術運算符
- Java 語句初學者教程
- 用 Java 創建對象的不同方法
- 內部類
- 字符串構建器
- Java 字符串教程
- Java 教程 – 變量
- Java 中的變量
- Java 中的局部變量
- Java 中的實例變量
- Java 引用變量
- 變量遮蓋
- Java 教程 – 循環
- Java for循環
- Java 教程 – 異常
- Java 異常教程
- 異常處理 – try-with-resources語句
- Java 異常處理 – try catch塊
- Java 教程 – OOPS 概念
- Java 重載
- Java 方法覆蓋
- Java 接口
- 繼承
- Java 教程 – 關鍵字
- Java 中的this關鍵字
- Java static關鍵字
- Java 教程 – 集合
- Java 數組教程
- Java 集合
- Java 集合迭代器
- Java Hashmap教程
- 鏈表
- Java 初學者List集合教程
- Java 初學者的Map集合教程
- Java 初學者的Set教程
- Java 初學者的SortedSet集合教程
- Java 初學者SortedMap集合教程
- Java 教程 – 序列化
- Java 序列化概念和示例
- Java 序列化概念和示例第二部分
- Java 瞬態與靜態變量
- serialVersionUID的用途是什么
- Java 教程 – 枚舉
- Java 枚舉(enum)
- Java 枚舉示例
- 核心 Java 教程 – 線程
- Java 線程教程
- Java 8 功能
- Java Lambda:初學者指南
- Lambda 表達式簡介
- Java 8 Lambda 列表foreach
- Java 8 Lambda 映射foreach
- Java 9
- Java 9 功能
- Java 10
- Java 10 獨特功能
- 核心 Java 教程 – 高級主題
- Java 虛擬機基礎
- Java 類加載器
- Java 開發人員必須知道..
- Selenium 教程
- 1 什么是 Selenium?
- 2 為什么要進行自動化測試?
- 3 Selenium 的歷史
- 4 Selenium 工具套件
- 5 Selenium 工具支持的瀏覽器和平臺
- 6 Selenium 工具:爭霸
- 7A Selenium IDE – 簡介,優點和局限性
- 7B Selenium IDE – Selenium IDE 和 Firebug 安裝
- 7C Selenium IDE – 突破表面:初探
- 7D Selenium IDE – 了解您的 IDE 功能
- 7E Selenium IDE – 了解您的 IDE 功能(續)。
- 7F Selenium IDE – 命令,目標和值
- 7G Selenium IDE – 記錄和運行測試用例
- 7H Selenium IDE – Selenium 命令一覽
- 7I Selenium IDE – 設置超時,斷點,起點
- 7J Selenium IDE – 調試
- 7K Selenium IDE – 定位元素(按 ID,名稱,鏈接文本)
- 7L Selenium IDE – 定位元素(續)
- 7M Selenium IDE – 斷言和驗證
- 7N Selenium IDE – 利用 Firebug 的優勢
- 7O Selenium IDE – 以所需的語言導出測試用例
- 7P Selenium IDE – 其他功能
- 7Q Selenium IDE – 快速瀏覽插件
- 7Q Selenium IDE – 暫停和反射
- 8 給新手的驚喜
- 9A WebDriver – 架構及其工作方式
- 9B WebDriver – 在 Eclipse 中設置
- 9C WebDriver – 啟動 Firefox 的第一個測試腳本
- 9D WebDriver – 執行測試
- 9E WebDriver – 用于啟動其他瀏覽器的代碼示例
- 9F WebDriver – JUnit 環境設置
- 9G WebDriver – 在 JUnit4 中運行 WebDriver 測試
- 9H WebDriver – 隱式等待
- 9I WebDriver – 顯式等待
- 9J WebDriver – 定位元素:第 1 部分(按 ID,名稱,標簽名稱)
- 9K WebDriver – 定位元素:第 2 部分(按className,linkText,partialLinkText)
- 9L WebDriver – 定位元素:第 3a 部分(按cssSelector定位)
- 9M WebDriver – 定位元素:第 3b 部分(cssSelector續)
- 9N WebDriver – 定位元素:第 4a 部分(通過 xpath)
- 9O WebDriver – 定位元素:第 4b 部分(XPath 續)
- 9P WebDriver – 節省時間的捷徑:定位器驗證
- 9Q WebDriver – 處理驗證碼
- 9R WebDriver – 斷言和驗證
- 9S WebDriver – 處理文本框和圖像
- 9T WebDriver – 處理單選按鈕和復選框
- 9U WebDriver – 通過兩種方式選擇項目(下拉菜單和多項選擇)
- 9V WebDriver – 以兩種方式處理表
- 9W WebDriver – 遍歷表元素
- 9X WebDriver – 處理警報/彈出框
- 9Y WebDriver – 處理多個窗口
- 9Z WebDriver – 最大化窗口
- 9AA WebDriver – 執行 JavaScript 代碼
- 9AB WebDriver – 使用動作類
- 9AC WebDriver – 無法輕松定位元素? 繼續閱讀...
- 10A 高級 WebDriver – 使用 Apache ANT
- 10B 高級 WebDriver – 生成 JUnit 報告
- 10C 高級 WebDriver – JUnit 報表自定義
- 10D 高級 WebDriver – JUnit 報告自定義續
- 10E 高級 WebDriver – 生成 PDF 報告
- 10F 高級 WebDriver – 截屏
- 10G 高級 WebDriver – 將屏幕截圖保存到 Word 文檔
- 10H 高級 WebDriver – 發送帶有附件的電子郵件
- 10I 高級 WebDriver – 使用屬性文件
- 10J 高級 WebDriver – 使用 POI 從 excel 讀取數據
- 10K 高級 WebDriver – 使用 Log4j 第 1 部分
- 10L 高級 WebDriver – 使用 Log4j 第 2 部分
- 10M 高級 WebDriver – 以無頭模式運行測試
- Vue 教程
- 1 使用 Vue.js 的 Hello World
- 2 模板語法和反應式的初探
- 3 Vue 指令簡介
- 4 Vue Devtools 設置
- 5 數據綁定第 1 部分(文本,原始 HTML,JavaScript 表達式)
- 6 數據綁定第 2 部分(屬性)
- 7 條件渲染第 1 部分(v-if,v-else,v-else-if)
- 8 條件渲染第 2 部分(v-if和v-show)
- 9 渲染列表第 1 部分(遍歷數組)
- 10 渲染列表第 2 部分(遍歷對象)
- 11 監聽 DOM 事件和事件修飾符
- 12 監聽鍵盤和鼠標事件
- 13 讓我們使用簡寫
- 14 使用v-model進行雙向數據綁定
- 15 表單輸入綁定
- 18 類綁定
- Python 教程
- Python 3 簡介
- Python 基礎知識 - 又稱 Hello World 以及如何實現
- 如何在 Windows 中安裝 python
- 適用于 Windows,Mac,Linux 的 Python 設置
- Python 數字和字符串
- Python 列表
- Python 集
- Python 字典
- Python 條件語句
- Python 循環
- Python 函數
- 面向對象編程(OOP)
- Python 中的面向對象編程
- Python 3 中的異常處理
- Python 3:猜數字
- Python 3:猜數字 – 回顧
- Python 生成器
- Hibernate 教程
- Hibernate 框架基礎
- Hibernate 4 入門教程
- Hibernate 4 注解配置
- Hibernate 4 的實體關系
- Hibernate 4 中的實體繼承模型
- Hibernate 4 查詢語言
- Hibernate 4 數據庫配置
- Hibernate 4 批處理
- Hibernate 4 緩存
- Hibernate 4 審計
- Hibernate 4 的并發控制
- Hibernate 4 的多租戶
- Hibernate 4 連接池
- Hibernate 自舉