为什么 Reflection API 为现有的枚举构造函数抛出 NoSuchMethodException?



>UPDATE:我的问题与实例化枚举类无关。这个问题只需要使用现有值之一实例化枚举。我在问:为什么反射 API 会为真正存在的方法抛出NoSuchMethodException

以下代码运行时没有错误,具体取决于Xpto是声明为class还是enum

class Xpto {
// Bar; // include this for enum declaration
private Xpto() {      
}
}
public class App {
public static void main(String[] args) throws Exception{
Constructor<Xpto> constructor = Xpto.class.getDeclaredConstructor();
constructor.setAccessible(true);
constructor.newInstance();
}
}

在这两种情况下,javap都显示了构造函数private Xpto()。如果Xpto是一个类,则javap -private的结果是:

class Xpto {
private Xpto();
}

如果Xpto是枚举,则javap -private的结果为:

final class Xpto extends java.lang.Enum<Xpto> {
...
private Xpto();
static {};
}

然而,对于后者,它抛出了一个例外:

Exception in thread "main" java.lang.NoSuchMethodException: Xpto.<init>()
at java.lang.Class.getConstructor0(Unknown Source)

在这两种情况下,编译的结果都是具有私有构造函数。在Xpto.class.getDeclaredConstructor();中使用反射 API 不会报告有关Xpto是枚举这一事实的错误,而不是。它只是抛出没有这样的方法Xpto.<init>()枚举的情况。这不是真的。因为该构造函数存在。

以下是来自 Java 文档:

枚举

中的最终克隆方法确保枚举常量永远不能 被克隆,并通过序列化机制进行特殊处理 确保永远不会因以下原因创建重复实例 反序列化。禁止枚举类型的反射实例化。 这四件事一起确保没有枚举类型的实例 存在于枚举常量定义的范围之外。

编辑:经过一些额外的检查后完全改变了我的答案......

好吧,你的javap -private输出很奇怪,你应该验证它是否正确。我的输出是这样的:

final class Xpto extends java.lang.Enum{
public static final Xpto Bar;
private static final Xpto[] ENUM$VALUES;
static {};
private Xpto(java.lang.String, int); // see this line!
public static Xpto[] values();
public static Xpto valueOf(java.lang.String);
}

因此,编译器为您的enum真正创建的构造函数是一个双参数构造函数,采用Stringint(枚举名称和值(。这是合乎逻辑的,因为运行时所有酷枚举功能都需要名称和某种数字标识符。

因此,以这种方式更改代码会导致"更好"的错误消息:

Constructor<Xpto> constructor = Xpto.class.getDeclaredConstructor(String.class, int.class);
constructor.setAccessible(true);
constructor.newInstance("Foo", 2);

结果:

Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects
at java.lang.reflect.Constructor.newInstance(Constructor.java:417)

因为你尝试从静态方法访问非静态内部类,所以它很正常

最新更新