本文试图教会你使用 枚举 Enum,进来了解一下?

3,921 阅读7分钟

枚举是 Java 中的一种特殊类型,它用于表示一组固定值。枚举中定义的每个值称为一个枚举常量,它们在声明时都必须赋予一个固定的值,即枚举值。相比于使用常量或者 final 字段来表示一组固定值,枚举更加类型安全、易读、易维护。

Java 中的枚举类型是通过关键字 enum 来定义的。一个简单的示例代码如下:

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

这个示例代码定义了一个 WeekDay 枚举类型,其中包含了一周中的七个枚举常量。需要注意的是,枚举常量名称通常使用大写字母,这是一种约定俗成的规范。

在使用枚举类型时,可以直接使用枚举常量来表示对应的值。例如,可以定义一个方法来判断指定的日期是不是工作日:

public class Util {
    public static boolean isWorkday(WeekDay day) {
        switch (day) {
            case MONDAY:
            case TUESDAY:
            case WEDNESDAY:
            case THURSDAY:
            case FRIDAY:
                return true;
            default:
                return false;
        }
    }
}

在这个示例代码中,isWorkday() 方法接受一个 WeekDay 枚举常量作为参数,根据该参数判断当前日期是否是工作日。由于 WeekDay 中的枚举常量是固定不变的,因此这种方式可以有效避免传入无效的值或者字符串拼写错误等问题。

除了定义简单的枚举类型之外,Java 中的枚举还支持很多高级特性。

枚举构造函数和字段

每个枚举常量都实际上是一个枚举实例,因此可以定义构造函数来初始化它们的状态。枚举构造函数只能是 private 或者 package-private(默认访问权限),这是因为枚举常量的创建只能在枚举类内部进行。

例如,可以在 WeekDay 枚举中添加一个字段用于表示每个工作日的工作时间长度:

public enum WeekDay {
    MONDAY(8), TUESDAY(8), WEDNESDAY(8), THURSDAY(8), FRIDAY(8),
    SATURDAY(0), SUNDAY(0);
    
    private final int workHours;
    
    private WeekDay(int workHours) {
        this.workHours = workHours;
    }
    
    public int getWorkHours() {
        return workHours;
    }
}

在这个示例代码中,WeekDay 枚举定义了一个 workHours 字段,用于表示每个工作日的工作时间长度。通过添加一个构造函数,可以将每个枚举常量的工作时间长度初始化为对应的值。

枚举实现接口

Java 中的枚举还可以实现一个或多个接口,这使得枚举常量可以像普通类一样拥有方法和行为。例如,可以定义一个接口来表示一种颜色,然后让枚举常量实现该接口:

public interface Color {
    String getName();
}

public enum Rainbow implements Color {
    RED("红"), ORANGE("橙"), YELLOW("黄"), GREEN("绿"),
    CYAN("青"), BLUE("蓝"), PURPLE("紫");
    
    private final String name;
    
    private Rainbow(String name) {
        this.name = name;
    }
    
    @Override
    public String getName() {
        return name;
    }
}

在这个示例代码中,Rainbow 枚举实现了一个 Color 接口,该接口定义了 getName() 方法。通过实现该接口,Rainbow 枚举常量可以返回对应颜色的名称。

枚举的比较和排序

枚举常量之间的比较可以使用 "== "运算符,这是因为每个枚举常量实际上都是一个独立的对象。例如,判断两个枚举常量是否相等可以使用以下语句:

if (day == WeekDay.MONDAY) { ... }

枚举类型还支持比较操作,包括 equals() 方法和 compareTo() 方法。默认情况下,枚举常量的比较顺序是它们在枚举中的声明顺序,也可以通过实现 Comparable 接口来自定义枚举常量的比较方式。

例如,可以对 WeekDay 枚举实现 Comparable 接口,按照工作日的顺序进行排序:

public enum WeekDay implements Comparable<WeekDay> {
    MONDAY(8), TUESDAY(8), WEDNESDAY(8), THURSDAY(8), FRIDAY(8),
    SATURDAY(0), SUNDAY(0);
    
    ...
    
    @Override
    public int compareTo(WeekDay other) {
        return Integer.compare(this.workHours, other.workHours);
    }
}

在这个示例代码中,WeekDay 枚举实现了 Comparable 接口,重写了 compareTo() 方法,按照每个工作日的工作时间长度进行比较。通过在 compareTo() 方法中调用 Integer.compare() 方法,实现了按照整数值进行比较。

枚举的序列化和反序列化

Java 中的枚举类型可以进行序列化和反序列化,如在网络传输或保存到文件中。由于枚举常量在一个程序中是固定不变的,因此只需要序列化枚举常量的名称即可。

例如,可以使用 ObjectOutputStream 将 WeekDay 枚举序列化到文件中:

try (ObjectOutputStream out = new ObjectOutputStream(
        new FileOutputStream("weekdays.bin"))) {
    out.writeObject(WeekDay.MONDAY);
} catch (IOException ex) {
    ex.printStackTrace();
}

在反序列化时,可以使用 ObjectInputStream 读取枚举常量的名称,并通过 Enum.valueOf() 方法将其转换为对应的枚举常量:

try (ObjectInputStream in = new ObjectInputStream(
        new FileInputStream("weekdays.bin"))) {
    WeekDay day = WeekDay.valueOf(in.readUTF());
    System.out.println(day);
} catch (IOException | ClassNotFoundException ex) {
    ex.printStackTrace();
}

在这个示例代码中,先使用 readUTF() 方法读取序列化后的枚举常量名称(这里是 "MONDAY"),然后通过 valueOf() 方法将其转换为 WeekDay 枚举常量。

代码示例

颜色枚举

public enum Color {
    RED("#FF0000"), GREEN("#00FF00"), BLUE("#0000FF");
    
    private final String code;
    
    private Color(String code) {
        this.code = code;
    }
    
    public String getCode() {
        return code;
    }
}

这个示例代码定义了一个简单的 Color 枚举类型,其中包含三个颜色常量,每个常量都有一个对应的色彩代码。在使用时可以使用 Color.RED.getCode() 来获取红色的色彩代码。

状态枚举

public enum Status {
    NEW("新建"), OPEN("已开启"), CLOSED("已关闭");
    
    private final String desc;
    
    private Status(String desc) {
        this.desc = desc;
    }
    
    public String getDesc() {
        return desc;
    }
}

这个示例代码定义了一个简单的 Status 枚举类型,其中包含三个状态常量,每个常量都有一个对应的状态描述。在使用时可以使用 Status.NEW.getDesc() 来获取新建状态的描述。

星期枚举

public enum WeekDay {
    MONDAY(8), TUESDAY(8), WEDNESDAY(8), THURSDAY(8), FRIDAY(8),
    SATURDAY(0), SUNDAY(0);
    
    private final int workHours;
    
    private WeekDay(int workHours) {
        this.workHours = workHours;
    }
    
    public int getWorkHours() {
        return workHours;
    }
    
    public static boolean isWorkday(WeekDay day) {
        return day != SATURDAY && day != SUNDAY;
    }
}

这个示例代码定义了一个 WeekDay 枚举类型,其中包含七个常量,前五个为工作日,后两个为非工作日。每个工作日都有一个对应的工作时间长度,在 isWorkday() 方法中判断一个指定的日期是否为工作日。

季节枚举

public enum Season {
    SPRING("春", 1), SUMMER("夏", 2), AUTUMN("秋", 3), WINTER("冬", 4);
    
    private final String name;
    private final int index;
    
    private Season(String name, int index) {
        this.name = name;
        this.index = index;
    }
    
    public String getName() {
        return name;
    }
    
    public int getIndex() {
        return index;
    }
    
    public static Season getByIndex(int index) {
        for (Season season : values()) {
            if (season.getIndex() == index) {
                return season;
            }
        }
        throw new IllegalArgumentException("Invalid index: " + index);
    }
}

这个示例代码定义了一个 Season 枚举类型,其中包含四个季节常量,每个常量都有一个对应的名称和索引。通过 getByIndex() 方法可以根据索引获取对应的季节枚举值。

交通信号灯枚举

public enum TrafficLight {
    RED(30), GREEN(60), YELLOW(5);
    
    private final int seconds;
    
    private TrafficLight(int seconds) {
        this.seconds = seconds;
    }
    
    public int getSeconds() {
        return seconds;
    }
    
    public TrafficLight next() {
        switch (this) {
            case RED:
                return GREEN;
            case GREEN:
                return YELLOW;
            case YELLOW:
                return RED;
            default:
                throw new IllegalStateException("Invalid state");
        }
    }
    
    @Override
    public String toString() {
        return name() + "(" + seconds + ")";
    }
}

这个示例代码定义了一个 TrafficLight 枚举类型,其中包含三个常量,分别表示红、绿、黄三种交通信号灯。该枚举实现了 next() 方法,返回下一个状态,以及 toString() 方法,返回对应的信号灯名称和持续时间。在使用时可以使用 TrafficLight.RED.getSeconds() 来获取红灯的持续时间。

总结

枚举类型是 Java 中的一种特殊类型,它用于表示一组固定值。Java 枚举类型具有以下优点:

  • 类型安全:枚举类型可以避免传入无效的值或者字符串拼写错误等问题。
  • 易读易维护:通过采用命名约定和语义约定,使得代码更加可读、易于理解和维护。
  • 扩展性:枚举类型支持定义方法、字段、接口等高级特性,使得枚举常量能够像普通类一样拥有行为和状态。

Java 枚举类型的使用场景包括但不限于:

  • 状态码、状态标识等一组固定值的表示。
  • 星期、月份、季节等一组固定值的表示。
  • 颜色、性别、血型等一组固定取值的表示。
  • 开关、状态机、状态转换等复杂业务逻辑的建模。

总之,在选择使用枚举类型时需要考虑其表达意义的稳定性和清晰度,以及代码的可读性和可维护性。