前言

在本文中,我们将看到什么是 Java 枚举类,以及它的使用场景和注意事项。

版本约定

Java 枚举是一个特殊的类,一般表示一组常量,比如一年的 4 个季节,一个年的 12 个月份,一个星期的 7 天,方向有东南西北等。

Java 枚举类使用 enum 关键字来定义,各个常量使用逗号分割。比如,定义个星期的枚举类:

  1. public enum Weekday {
  2. SUN, MON, TUE, WED, THU, FRI, SAT;
  3. }

枚举类通常用来定义常量,可以将有关联关系的常量组织起来,使代码更加易读、安全,并且还可以使用枚举提供的方法。

枚举的比较

使用 enum 定义的枚举类是一种引用类型。前面我们讲过,引用类型比较,要使用 equals() 方法,如果使用 == 比较,它比较的是两个引用类型的变量是否指向同一个对象。因此,引用类型比较,要使用 equals() 方法,但 enum 类型可以例外。

这是因为 enum 类型的每个常量在 JVM 中只有一个唯一实例,所以可以直接用 == 比较:

  1. if (day == Weekday.FRI) { // ok!
  2. }

枚举的本质

通过 enum 定义的枚举类,和其他的 class 有什么区别?

答案是没有任何区别。enum 定义的类型就是 class,只不过它有以下几个特点:

  • 定义的 enum 类型总是继承自 java.lang.Enum,且无法被继承;
  • 只能定义出 enum 的实例,而无法通过 new 操作符创建 enum 的实例;
  • 定义的每个实例都是引用类型的唯一实例;
  • 可以将 enum 类型用于 switch 语句。

例如,我们定义的 Weekday 枚举类:

  1. public enum Weekday {
  2. SUN, MON, TUE, WED, THU, FRI, SAT;
  3. }

编译器编译出的 class 大概就像这样:

  1. // 继承自 Enum,标记为 final class
  2. public final class Weekday extends Enum
  3. {
  4. public static Weekday[] values()
  5. {
  6. return (Weekday[])$VALUES.clone();
  7. }
  8. public static Weekday valueOf(String name)
  9. {
  10. return (Weekday)Enum.valueOf(test12/Weekday, name);
  11. }
  12. // private 构造方法,确保外部无法调用 new 操作符:
  13. private Weekday(String s, int i)
  14. {
  15. super(s, i);
  16. }
  17. // 每个实例均为全局唯一:
  18. public static final Weekday SUN;
  19. public static final Weekday MON;
  20. public static final Weekday TUE;
  21. public static final Weekday WED;
  22. public static final Weekday THU;
  23. public static final Weekday FRI;
  24. public static final Weekday SAT;
  25. private static final Weekday $VALUES[];
  26. static
  27. {
  28. SUN = new Weekday("SUN", 0);
  29. MON = new Weekday("MON", 1);
  30. TUE = new Weekday("TUE", 2);
  31. WED = new Weekday("WED", 3);
  32. THU = new Weekday("THU", 4);
  33. FRI = new Weekday("FRI", 5);
  34. SAT = new Weekday("SAT", 6);
  35. $VALUES = (new Weekday[] {
  36. SUN, MON, TUE, WED, THU, FRI, SAT
  37. });
  38. }
  39. }

所以,编译后的 enum 类和普通 class 并没有任何区别。但是我们自己无法按定义普通 class 那样来定义 enum,必须使用 enum 关键字,这是 Java 语法规定的。

枚举的方法

根据编译后的源码,我们知道 enum 类型总是继承自 java.lang.Enum,它提供了一些基本方法:

  • values():返回 enum 实例的数组,而且该数组中的元素严格保持在 enum 中声明时的顺序;
  • valueOf():返回带指定名称的指定枚举类型的枚举常量;
  • name():返回实例名;
  • ordinal():返回实例声明时的次序,从 0 开始;
  • getDeclaringClass():返回实例所属的 enum 类型;
  • equals():判断是否为同一个对象。

此外,java.lang.Enum 实现了 Comparable 和 Serializable 接口,所以也提供 compareTo() 方法。

自定义属性和方法

我们在使用枚举的时候不会只定义枚举实例名,还会定义一些属性和方法,方便我们使用。

枚举可以添加普通方法、静态方法、抽象方法、构造方法,枚举也可以实现接口,但是,枚举不能继承。

比如上面的 Weekday 枚举,我们可以添加如下属性和方法:

  1. public enum Weekday {
  2. SUN(0, "Sunday"),
  3. MON(1, "Monday"),
  4. TUE(2, "Tuesday"),
  5. WED(3, "Wednesday"),
  6. THU(4, "Thursday"),
  7. FRI(5, "Friday"),
  8. SAT(6, "Saturday");
  9. private final int index;
  10. private final String name;
  11. private Weekday(int index, String name) {
  12. this.index = index;
  13. this.name = name;
  14. }
  15. public static String getNameByIndex(int index) {
  16. for (Weekday weekday : values()) {
  17. if (weekday.getIndex() == index) {
  18. return weekday.getName();
  19. }
  20. }
  21. return null;
  22. }
  23. public int getIndex() {
  24. return index;
  25. }
  26. public String getName() {
  27. return name;
  28. }
  29. }

总结

  • Java 使用 enum 定义枚举类型,它被编译器编译为 final class Xxx extends Enum { … };
  • 可以为 enum 编写构造方法、字段和方法;
  • enum 的构造方法要声明为 private,字段建议声明为 private final。

    转载

  • 枚举类

  • 深入理解 Java 枚举

作者:殷建卫 链接:https://www.yuque.com/yinjianwei/vyrvkf/wy3oy3 来源:殷建卫 - 开发笔记 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。