在字节码级别,Java 的 Class.getEnumConstants() 如何知道哪些 类 是枚举 类?

At the bytecode level, how does Java's Class.getEnumConstants() know which classes are enum classes?

Java 反射 API 包含一个方法 Class.getEnumConstants() 可以确定 class 是否是 enum class(它 returns null 如果它不认为 class 是一个 enum),以及它的常量是什么。

我正在开发一个直接生成 JVM 字节码的程序,并试图生成一个枚举 class。因此,我需要知道 Java 如何从它们的字节码中识别枚举 classes,以便 getEnumConstants 能够正常工作。显然,class 需要扩展 Enum,但这显然是不够的(例如,对应于 public class Example extends Enum<Example> {} 的字节码将不会被识别为 enum) ; class 的 JVM 字节码还需要具备哪些其他特性,以便 Java 的反射 API 将其识别为 Java enum,并成为能够确定其枚举常量?

为了编译 enum 类型,您必须在 class' 访问标志中用 ACC_ENUM 标志标记 class。

此外,对于每个常量,您必须创建一个相应的 public static final 字段,该字段的访问标志中也标有 ACC_ENUM

然后,您需要一个 class initializer(一个名为 <clinit> 的无参数 void 方法)来创建实例并将它们分配给字段。

但这还不够。 mindthe language specification,指定存在两个隐式声明的方法

  /**
  * Returns an array containing the constants of this enum 
  * type, in the order they're declared.  This method may be
  * used to iterate over the constants as follows:
  *
  *    for(E c : E.values())
  *        System.out.println(c);
  *
  * @return an array containing the constants of this enum 
  * type, in the order they're declared
  */
  public static E[] values();

  /**
  * Returns the enum constant of this type with the specified
  * name.
  * The string must match exactly an identifier used to declare
  * an enum constant in this type.  (Extraneous whitespace 
  * characters are not permitted.)
  * 
  * @return the enum constant with the specified name
  * @throws IllegalArgumentException if this enum type has no
  * constant with the specified name
  */
  public static E valueOf(String name);

这是编译器的职责。字节码生成工具将它们的实现插入到特定的枚举类型中。请注意,尽管是编译器生成的,这两个方法 should not get marked as synthetic.


规范没有说明反射将如何收集其信息。它可以遍历标记的字段并将它们读取到 assemble 数组,或者它可以只调用特定类型的 values() 方法。因此,您不能省略任何这些工件,也不能通过仅委托给 Class.getEnumConstants().

来实现 values() 方法