详解Java枚举类型(Enum)中的方法

标签: Java

保留所有版权,请引用而不是转载本文(原文地址 https://yeecode.top/blog/07/ )。

在上篇文章中,我们对Java中的枚举类进行了详细的介绍。

对于Enum还不了解的小伙伴,可以先预习《Java中的枚举类型(Enum)详解》一文。

通过反编译,我们知道Java枚举类会在编译之后转化为一个继承了java .lang.Enum的类,而我们定义的每个枚举值都会在类的初始化阶段被实例化为我们所定义的枚举类的一个对象。

在枚举类被编译之后,有一些方法是编译器在编译阶段写入的,那这些方法有什么特点?枚举类中还有一些继承来的方法,它们又有哪些?枚举类中的枚举值是在编译阶段被创建为对象,那构造函数又在哪?

这篇文章我们将详细分析。

Enum抽象类常见方法

我们上篇文章已经讲过,枚举类实际上继承了Enum抽象类,因此Enum抽象类是所有枚举类型的基本类,下面是它的常见方法。

需要再次说明的是,以上的方法都是Enum抽象类的方法,会被Enum的对象继承,而不是Enum的静态方法。而最终枚举值被实例化成了Enum对象,所以,枚举值拥有以上的方法。

这一块比较简单,我们直接举例子说明:

首先我们定义一个最简单的枚举类:

public enum  Season {
    SPRING,
    SUMMER,
    AUTUMN,
    WINTER
}

之后我们在定义一个附带属性的枚举类:

public enum StatusEnum {

    DOING("进行中", "DOING"),
    DONE("已完成", "DONE"),
    CANCELLED("已取消", "CANCELLED");

    private String name;
    private String status;

    StatusEnum(String name, String status) {
        this.name = name;
        this.status = status;
    }

   // 省略name/status的getter/setter

接下来,我们写方法进行试验:

public void doTest() {
    Season[] Seasons = Season.values();
    for (Season Season : Seasons) {
        System.out.print("toString:" + Season.toString());
        System.out.print("; name:" + Season.name());
        System.out.println("; ordinal:" + Season.ordinal());
    }

    System.out.println("----");
    System.out.println(Season.valueOf("AUTUMN").name());
    System.out.println("----");
    Class clazz = Season.SUMMER.getDeclaringClass();
    System.out.println(clazz.getName());
    System.out.println("----");
    System.out.println(StatusEnum.CANCELLED.name());// 这是Enum自有的方法
    System.out.println(StatusEnum.CANCELLED.getName()); // 这是我们写的getter方法
}

最后结果:

toString:SPRING; name:SPRING; ordinal:0
toString:SUMMER; name:SUMMER; ordinal:1
toString:AUTUMN; name:AUTUMN; ordinal:2
toString:WINTER; name:WINTER; ordinal:3
----
AUTUMN
----
com.test.constant.Season
----
CANCELLED
已取消

我们可以看到,对于每个枚举值,可以调用上述的继承自Enum抽象类的方法。

枚举类型的构造函数

既然枚举值是由编译器创建为枚举类型的实例,那它必然调用了构造函数。那该函数在哪呢?我们能不能调用呢?

其实该构造函数也在Enum抽象类中。

/**
 - Sole constructor.  Programmers cannot invoke this constructor.
 - It is for use by code emitted by the compiler in response to
 - enum type declarations.
 *
 - @param name - The name of this enum constant, which is the identifier
 -               used to declare it.
 - @param ordinal - The ordinal of this enumeration constant (its position
 -         in the enum declaration, where the initial constant is assigned
 -         an ordinal of zero).
 */
protected Enum(String name, int ordinal) {
    this.name = name;
    this.ordinal = ordinal;
}

我们特意将方法注释也保留了下来,可以看到,该方法只能由编译器调用,开发人员无法调用。所以说,我们还是不要操心了,只需要定义好枚举类型,剩下的交给编译器。

再论编译器插入的静态方法

我们知道values()方法和valueOf(String s)方法是由编译器插入到枚举类中的静态方法。这总让人觉得怪异。而同时,我们知道枚举类型中的每一个枚举值也在编译阶段被声明为了一个枚举类。关于这几点,我们在上一篇文章中已经详细分析,大家可以回上篇文章找回记忆。我们直接贴出通过字节码推出的代码,如下:

public final class com.test.constant.Season extends java
.lang.Enum<com.test.constant.Season> {
  public static final com.test.constant.Season SPRING;
  public static final com.test.constant.Season SUMMER;
  public static final com.test.constant.Season AUTUMN;
  public static final com.test.constant.Season WINTER;

  private static final com.test.constant.Season[] $VALUES;

  public static com.test.constant.Season[] values(){
        return (Season[])$VALUES.clone();
  }

  public static com.test.constant.Season valueOf(String s){
        return (Season)Enum.valueOf(java/lang/String, s);
    }

  private com.test.constant.Season(String s, int i){
    super(s,i);
  }

  static {
    SPRING = new Season("SPRING", 0);
    SUMMER = new Season("SUMMER", 1);
    AUTUMN = new Season("AUTUMN", 2);
    WINTER = new Season("WINTER", 3);
        $VALUES = (new Season[] {
            SPRING, SUMMER, AUTUMN, WINTER
        });
    }
}

有人会这么认为:

我对此存疑,因为无论是原生的Season枚举类还是Season.AUTUMN向上转型成的Season枚举类,本质上是同一个枚举类。因此,都应该可以调用values()方法和valueOf(String s)方法。

对此,我们进行验证:

public class MainTest {
    public static void main(String[] args) {
        System.out.println("原生的枚举类:" + Season.class.getName());
        System.out.println(Season.values()[1]);

        Season s = Season.AUTUMN;
        System.out.println("枚举值向上转型出的枚举类:" + s.getClass().getName());
        System.out.println(s.values()[1]);
    }
}

得到如下输出:

原生的枚举类:com.test.constant.Season
SUMMER
枚举值向上转型出的枚举类:com.test.constant.Season
SUMMER

证明了笔者的猜测。

总结

通过该文章,我们对枚举类中的方法进行了全面的了解:

这样,通过《Java中的枚举类型(Enum)详解》和本篇文章,我们对枚举类型的原理和方法有了详细的了解。接下来,我们还会有一篇文章介绍枚举类型的使用,从而从原理、特性、使用三个方面对枚举类型进行详细的介绍,欢迎继续关注。

本文首发于个人知乎:易哥(https://www.zhihu.com/people/yeecode),欢迎关注。

作者书籍推荐