JAVA-枚举使用

时间:2022-12-14 16:07:39

枚举

在本教程中,我们将了解什么是 Java 枚举、它们解决的问题以及它们的一些设计模式如何在实践中使用。

1. 概述

Java 5 首先引入了 enum 关键字。它表示一种特殊类型的类,它总是扩展 java.lang.Enum 类。有关使用的官方文档,我们可以转到​​文档​​。 以这种方式定义的常量使代码更具可读性,允许进行编译时检查,预先记录可接受值的列表,并避免由于传入无效值而导致的意外行为。

2. 定义

最简单的定义一个枚举类。


public enum WeekDay {    
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY,
SUNDAY
}

3. 自定义枚举方法

下面这个方法,会根据参数local进行判断,如果是"CN"返回值就加1,如果不是就直接返回序数。


int daysOrderInWeek(String local) {
if (local.equals("CN")) {
return ordinal() + 1;
}
return ordinal();
}


public static void main(String[] args) {
System.out.println(WeekDay.MONDAY.daysOrderInWeek("CN"));
System.out.println(WeekDay.MONDAY.daysOrderInWeek("EN"));
}

// 结果
// 1
// 0

可以看到上面的方法是使用枚举中定义的值来访问,如果需要定义枚举类访问的方法,要用static修饰,定义一个静态方法。比如:


static WeekDay firstDayInWeek(String local) {
if (local.equals("CN")) {
return MONDAY;
}
return SUNDAY;
}

// 测试
System.out.println(WeekDay.firstDayInWeek("CN"));
System.out.println(WeekDay.firstDayInWeek("US"));
// 结果
// MONDAY
// SUNDAY

4. 使用“==”比较枚举

由于枚举类型确保JVM中只有一个常量实例,因此我们可以安全地使用“==”运算符来比较两个变量。此外,“==”运算符提供编译时和运行时安全性。


WeekDay firstDayUS = WeekDay.firstDayInWeek("US");
if (firstDayUS == WeekDay.SUNDAY) {
System.out.println("对的,就是这样。");
}

5. 在switch中使用

非常方便和简单。


public void tellItLikeItIs() {
switch (day) {
case MONDAY -> System.out.println("周一很糟糕。");
case FRIDAY -> System.out.println("周五非常好。");
case SATURDAY, SUNDAY -> System.out.println("周末是最好的。");
default -> System.out.println("周中的日子一般般。");
}
}

6. 枚举的字段、方法以及构造函数


public class Pizza {

private PizzaStatus status;
public enum PizzaStatus {
ORDERED (5){
@Override
public boolean isOrdered() {
return true;
}
},
READY (2){
@Override
public boolean isReady() {
return true;
}
},
DELIVERED (0){
@Override
public boolean isDelivered() {
return true;
}
};

private int timeToDelivery;

public boolean isOrdered() {return false;}

public boolean isReady() {return false;}

public boolean isDelivered(){return false;}

public int getTimeToDelivery() {
return timeToDelivery;
}

PizzaStatus (int timeToDelivery) {
this.timeToDelivery = timeToDelivery;
}
}

public boolean isDeliverable() {
return this.status.isReady();
}

public void printTimeToDeliver() {
System.out.println("Time to delivery is " +
this.getStatus().getTimeToDelivery());
}

// Methods that set and get the status variable.
}

测试上面的代码。


@Test
public void givenPizaOrder_whenReady_thenDeliverable() {
Pizza testPz = new Pizza();
testPz.setStatus(Pizza.PizzaStatus.READY);
assertTrue(testPz.isDeliverable());
}

7. EnumSet和EnunMap

7.1 EnumSet

EnumSet 是专门用于 Enum 类型的 Set 实现。


EnumSet<WeekDay> weekends = EnumSet.of(WeekDay.SATURDAY, WeekDay.SUNDAY);
for (WeekDay weekend : weekends) {
System.out.printf("今天是周末:%s\n", weekend.name());
}

List<WeekDay> weekDays = Arrays.stream(WeekDay.values()).filter(WeekDay::isWeekends).toList();
System.out.println(weekDays.size() == weekends.size());

7.2 EnumMap

EnumMap 是一种专门的 Map 实现,旨在与枚举常量一起用作键。与其对应的 HashMap 相比,它是一种高效且紧凑的实现,在内部表示为数组。

定义的时候需要指定keyType


EnumMap<WeekDay, String> nameMap = new EnumMap<>(WeekDay.class);
nameMap.put(WeekDay.MONDAY, "周一");
nameMap.put(WeekDay.TUESDAY, "周二");
nameMap.put(WeekDay.WEDNESDAY, "周三");
nameMap.put(WeekDay.THURSDAY, "周四");

System.out.println(nameMap.get(WeekDay.MONDAY));

8. 使用枚举实现单例模式

通常,使用单例模式实现一个类是非常重要的。枚举提供了一种实现单例的快速简便的方法。

此外,由于枚举类在底层实现了 Serializable 接口,因此 JVM 保证该类是单例。这与传统实现不同,在传统实现中,我们必须确保在反序列化期间不创建新实例。


public enum EnumSingleton {
INSTANCE;

public static EnumSingleton getInstance() {
return INSTANCE;
}

private final WeekDay weekDay = WeekDay.SUNDAY;

public String sundayName() {
return weekDay.name();
}
}

9. 使用枚举实现策略模式

通常,策略模式是通过具有由不同类实现的接口来编写的。 添加新策略意味着添加新的实现类。

使用枚举,我们可以更轻松地实现这一目标,添加新的实现意味着只需定义另一个具有某种实现的实例。


public enum CalculatorEnum {
ADD("+") {
@Override
public int exec(int a, int b) {
return a + b;
}
},
SUB("-") {
@Override
public int exec(int a, int b) {
return a - b;
}
},
MUL("*") {
@Override
public int exec(int a, int b) {
return a * b;
}
};

private final String value;

CalculatorEnum(String value) {
this.value = value;
}

public String getValue() {
return this.value;
}

public abstract int exec(int a, int b);

}

测试一下运行结果:

JAVA-枚举使用

public class StrategyTest {
public static void main(String[] args) {
int a = 2;
int b = 3;

System.out.println("两数相加等于:" + exec("+", a, b));
System.out.println("两数相减等于:" + exec("-", a, b));
System.out.println("两数相乘等于:" + exec("*", a, b));
}

private static int exec(String symbol, int a, int b) {
if (symbol.equals(CalculatorEnum.ADD.getValue())) {
return CalculatorEnum.ADD.exec(a, b);
} else if (symbol.equals(CalculatorEnum.SUB.getValue())) {
return CalculatorEnum.SUB.exec(a, b);
} else if (symbol.equals(CalculatorEnum.MUL.getValue())) {
return CalculatorEnum.MUL.exec(a, b);
}
return -1;
}
}

策略枚举是一个非常优秀和方便的模式,但是它受到枚举类型的限制,每个枚举项都是public、final、static的,扩展性收到了一定的约束,因此在系统开发中,策略枚举一般担当不经常发生变化的角色。——《设计模式之禅》

10. 枚举的JSON表示

使用 Jackson 库,可以使用 JSON 表示枚举类型,就好像它们是 POJO 一样。


// 使用注释 修饰枚举类
@JsonFormat(shape = JsonFormat.Shape.OBJECT)

// 修饰字段
@JsonProperty("fieldName")

有关枚举类型的 JSON 序列化/反序列化(包括自定义)的更多信息,我们可以参考 ​​Jackson​​。