代码改变世界

java enum枚举实现机制

2024-01-27 13:19  youxin  阅读(26)  评论(0编辑  收藏  举报

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

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

通过反编译,我们知道Java枚举类会在编译之后转化为一个继承了java

.lang.Enum的类,而我们定义的每个枚举值都会在类的初始化阶段被实例化为我们所定义的枚举类的一个对象。

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

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

1 Enum抽象类常见方法

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

  • ordinal()方法:该方法获取的是枚举变量在枚举类中声明的顺序,下标从0开始,如日期中的MONDAY在第一个位置,那么MONDAY的ordinal值就是0,如果MONDAY的声明位置发生变化,那么ordinal方法获取到的值也随之变化,注意在大多数情况下我们都不应该首先使用该方法,毕竟它总是变幻莫测的。
  • compareTo(E o)方法:则是比较枚举的大小,注意其内部实现是根据每个枚举的ordinal值大小进行比较的。
  • name()方法与toString():几乎是等同的,都是输出变量的字符串形式。
  • getDeclaringClass(): 返回该枚举变量所在的枚举类。

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

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

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

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

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

最后结果:

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

2 枚举类型的构造函数

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

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

 

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

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

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

有人会这么认为:

  • 原来的Season枚举类中被编译器插入了values()方法和valueOf(String s)方法,因此能够正常调用Season.values()
  • 如果我们使用某个枚举值,如Season.AUTUMN向上转型成为Season枚举类,则无法调用values()方法和valueOf(String s)方法

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

对此,我们进行验证:

得到如下输出:

证明了笔者的猜测。

4 总结

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

  • 枚举类在编译阶段会被编译器插入一些静态方法
  • 枚举类本身有个只有编译器能够调用的构造方法,编译器会使用该方法将枚举值实例化为枚举类型的对象
  • 枚举值被实例化后,继承了众多java.lang.Enum中的方法

这样,通过《Java中的枚举类型(Enum)详解》和本篇文章,我们对枚举类型的原理和方法有了详细的了解。

转:https://zhuanlan.zhihu.com/p/64604609

 

jdk文档 enum抽象类:

https://www.runoob.com/manual/jdk11api/java.base/java/lang/Enum.html

public abstract class Enum<E extends Enum<E>>
implements Comparable<E>, Serializable {
/**
* The name of this enum constant, as declared in the enum declaration.
* Most programmers should use the {@link #toString} method rather than
* accessing this field.
*/
private final String name;


public abstract class Enum<E extends Enum<E>>{
public final int compareTo( E o) { ... }
}

Enum的源码中<E extends Enum<E>>这种格式的泛型是指:
该类只能被子类实例化,子类会继承其方法.就是类型E是Enum的子类.而该类的compareTo方法参数就是类型E,既子类.

 

下面做个标志,方便理解

考虑下面的语法

E extends Foo<E>

首先,这是一个泛型,使用E作为类型参数。
其次,对于E有上界限制,即E必须是Foo<E>的子类。

E被识别为Foo的子类,这个情况相当于把一个子类或者自己当成参数,传入到自身,引起一些特别的语法效果,

详细见:

解读 Enum<E extends Enum<E>>