JAVA基础-枚举

枚举

在本教程中,我们将了解什么是 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 weekends = EnumSet.of(WeekDay.SATURDAY, WeekDay.SUNDAY);
for (WeekDay weekend : weekends) {
    System.out.printf("今天是周末:%s\n", weekend.name());
}

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

7.2 EnumMap

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

定义的时候需要指定keyType

EnumMap 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);

}

测试一下运行结果:

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。

结语

参考链接:

  • https://docs.oracle.com/javase/tutorial/java/javaOO/enum.html
  • https://www.baeldung.com/a-guide-to-java-enums

你可能感兴趣的:(JAVA基础-枚举)