系列中的上一篇
当前教程
枚举

枚举

此页面由 Daniel SchmidUPL 下贡献

 

什么是枚举?

枚举是所有实例都为编译器已知的类。它们用于创建只能具有少数可能值的类型。

枚举的创建方式类似于类,但使用 enum 关键字而不是 class。在主体中,有一个枚举常量的实例列表,这些实例由 , 分隔。枚举的任何实例都不能在枚举常量之外创建。

public enum DayOfWeek {
    // enum constant are listed here:
    MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}

所有枚举都隐式扩展 java.lang.Enum 并且不能有任何子类。

 

访问、评估和比较枚举

枚举的值可以用作常量。为了检查两个枚举实例是否相同,可以使用 == 运算符。

DayOfWeek weekStart = DayOfWeek.MONDAY;

if (weekStart == DayOfWeek.MONDAY) {
    System.out.println("The week starts on Monday.");
}

也可以使用 switch 根据枚举的值执行操作。

DayOfWeek someDay = DayOfWeek.FRIDAY;

switch (someDay) {
    case MONDAY ->
        System.out.println("The week just started.");
    case TUESDAY, WEDNESDAY, THURSDAY ->
        System.out.println("We are somewhere in the middle of the week.");
    case FRIDAY ->
        System.out.println("The weekend is near.");
    case SATURDAY, SUNDAY ->
        System.out.println("Weekend");
    default ->
        throw new AssertionError("Should not happen");
}

使用 Switch 表达式,编译器可以检查枚举的所有值是否都已处理。如果 switch 表达式中缺少任何可能的值,则会发生编译错误。这被称为穷举性,也可以通过 密封类 在普通类中实现。

DayOfWeek someDay = DayOfWeek.FRIDAY;

String text = switch (someDay) {
    case MONDAY -> "The week just started.";
    case TUESDAY, WEDNESDAY, THURSDAY -> "We are somewhere in the middle of the week.";
    case FRIDAY -> "The weekend is near.";
    case SATURDAY, SUNDAY -> "Weekend";
};

System.out.println(text);

 

向枚举添加成员

与类一样,枚举也可以有构造函数、方法和字段。为了添加这些,需要在枚举常量列表之后添加一个 ;。构造函数的参数在枚举常量声明后的括号中传递。

public enum DayOfWeek {
    MONDAY("MON"), TUESDAY("TUE"), WEDNESDAY("WED"), THURSDAY("THU"), FRIDAY("FRI"), SATURDAY("SAT"), SUNDAY("SUN");
    
    private final String abbreviation;
    
    DayOfWeek(String abbreviation) {
        this.abbreviation = abbreviation;
    }
    
    public String getAbbreviation() {
        return abbreviation;
    }
}

 

特殊方法

所有枚举都有一些隐式添加的方法。

例如,name() 方法存在于所有枚举实例中,可用于获取枚举常量的名称。类似地,名为 ordinal() 的方法返回枚举常量在声明中的位置。

System.out.println(DayOfWeek.MONDAY.name());    // prints "MONDAY"
System.out.println(DayOfWeek.MONDAY.ordinal()); // prints "0" because MONDAY is the first constant in the DayOfWeek enum

除了实例方法之外,还向所有枚举添加了静态方法。values() 方法返回一个包含枚举所有实例的数组,valueOf(String) 方法可用于通过其名称获取特定实例。

DayOfWeek[] days = DayOfWeek.values(); // all days of the week
DayOfWeek monday = DayOfWeek.valueOf("MONDAY");

此外,枚举实现了 Comparable 接口。默认情况下,枚举按其序数排序,即按枚举常量出现的顺序排序。这允许比较枚举实例以及排序或搜索。

public void compareDayOfWeek(DayOfWeek dayOfWeek){
    int comparison = dayOfWeek.compareTo(DayOfWeek.WEDNESDAY);
    if ( comparison < 0) {
        System.out.println("It's before the middle of the work week.");
    } else if(comparison > 0){
        System.out.println("It's after the middle of the work week.");
    } else {
        System.out.println("It's the middle of the work week.");
    }
}
List<DayOfWeek> days = new ArrayList<>(List.of(DayOfWeek.FRIDAY, DayOfWeek.TUESDAY, DayOfWeek.SATURDAY));
Collections.sort(days);

 

使用枚举作为单例

由于枚举只能具有特定数量的实例,因此可以通过创建一个只有一个枚举常量的枚举来创建单例。

public enum SomeSingleton {
    INSTANCE;
    //fields, methods, etc.
}

 

枚举中的抽象方法

即使枚举不能扩展,它们仍然可以具有 abstract 方法。在这种情况下,每个枚举常量中都必须存在一个实现。

enum MyEnum {
    A() {
        @Override
        void doSomething() {
            System.out.println("a");
        }
    },
    B() {
        @Override
        void doSomething() {
            System.out.println("b");
        }
    };
    
    abstract void doSomething();
}

 

注意事项

在使用实例数量(或名称)可能发生变化的枚举时,应谨慎。每当枚举常量发生更改时,其他期望枚举旧版本的代码可能无法按预期工作。这可能表现为编译错误(例如,当引用已删除的枚举常量时)、运行时错误(例如,如果存在 default 案例,即使新的枚举常量应该单独处理)或其他不一致(例如,如果枚举的值已保存到文件,然后读取并期望该值仍然存在)。

更改枚举常量时,建议查看所有使用该枚举的代码。这在枚举也被其他人代码使用的情况下尤其重要。

此外,在存在大量实例的情况下,可能值得考虑使用其他选项,因为在代码中的单个位置列出大量实例可能不灵活。例如,在这种情况下,最好使用配置文件来列出所有实例,并在程序中读取这些配置文件。

 

结论

枚举提供了一种简单安全的方式来表示一组固定的常量,同时保留了类的大部分灵活性。它们是一种特殊的类,可用于编写优雅、可读且可维护的代码,并且与其他较新的现代功能(如 Switch 表达式)配合良好。另一种特殊的类是 Java 19 中引入的 Record 类。访问我们的 Records 教程 以了解更多信息。

要了解有关枚举的更多信息,请访问 java.lang.Enum javadoc。


上次更新: 2023 年 10 月 2 日


系列中的上一篇
当前教程
枚举