# 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 自举