为什么无法从枚举构造函数中调用超级构造函数?

9
public enum A {
     A(1);

     private A(int i){
     }

     private A(){
         super(); // compile - error
             // Cannot invoke super constructor from enum constructor A()
     }

}

以下是 enum A 继承自 abstract java.lang.Enum,而 abstract java.lang.Enum 又继承自 java.lang.Object

Class c = Class.forName("/*path*/.A");
System.out.println(c.getSuperclass().getName());
System.out.println(Modifier.toString(c.getSuperclass().getModifiers()).contains("abstract"));
System.out.println(c.getSuperclass().getSuperclass().getName());
4个回答

9

枚举在编译器和运行时层面暗示了很多神奇的东西,以确保 == 比较始终起作用:

显式实例化枚举类型(§15.9.1)是编译时错误。Enum 中的 final clone 方法确保枚举常量永远无法被克隆,并且序列化机制的特殊处理确保反序列化不会创建重复的实例。禁止通过反射实例化枚举类型。这四件事情一起确保枚举类型实例不存在于枚举常量定义之外。

Java 语言规范 第 8.9 节

枚举没有 parameterless 的 Enum() 构造函数,只有 Enum(String name, int ordinal)。但如果允许使用错误的参数调用该构造函数,显然会引起麻烦。


感谢您的评论。让我们看一个例子。抽象类A;和类B扩展了A {private B(){super()}}。你可以这样做,但不能在枚举中。为什么?A也没有任何构造函数,但是在运行时添加了super()。为什么枚举不行呢? - user467871
1
如果没有构造函数,Java将创建一个没有参数的构造函数。如果您添加了一个构造函数A(String bla),那么无参数的super()将不再起作用。枚举类型确实非常特殊,有很多隐藏的魔法正在进行。编译器/运行时会插入对super(name,index)的调用。这个调用使用正确的参数是至关重要的,因此编译器有一个特殊情况,防止手动调用超级构造函数。换句话说:枚举类型的内部是一个大而肮脏的黑客。 - Hendrik Brummermann

7

来自Java教程中的枚举:

所有枚举隐式地扩展java.lang.Enum。由于Java不支持多重继承,因此枚举不能扩展其他任何内容。

您无法调用super()构造函数,因为编译器会自动将对super(name, ordinal)的隐藏调用插入到您定义的任何构造函数中。


感谢您的评论。类A不继承任何其他类。只想调用super()来查看它的继承层次结构树。A->java.lang.Enum->java.lang.Object 因此,我有权调用super()到java.lang.Enum,然后super()到java.lang.Object。 - user467871
枚举类型是“特殊的”。你有权在普通类上调用super,但是枚举类型有更多的限制。 - dogbane
我的问题是,为什么? - user467871
为什么不呢?当语言本身并不需要时,为什么要增加额外的复杂性呢?也许你应该给我们一个有效的用例来说明想要这样做的原因。 - dogbane
我的意思是,这与面向对象的哲学相反。成为一个对象而不是(调用)对象?我认为枚举没有任何特殊原因,看看这个。抽象类A {};类B扩展A {private B(){super();}}所以它在这里也是无用的,但它是合法的。我认为背后有不同的逻辑,或者他们(Java核心开发人员)只是闲逛来编写它。 - user467871
你似乎没有意识到编译器会自动插入super()调用。这与“面向对象哲学”无关。 - user207421

1

事实上,你无法声明一个enum作为另一个类的扩展。因此,对于一个enum只有一个可能的super构造函数。

我猜,由于你不能链接到除了super()之外的任何东西,他们决定在enum构造函数中根本不允许使用super

无论哪种方式,你都不会失去任何东西。


我认为,当super()->super()->super()->Object到达Object时,对象的初始化已经完成。那么,如果不调用super()就无法到达Object,那么它怎么能够被初始化并从java.lang.Object扩展呢? - user467871
2
什么???我并没有说没有调用超级构造函数。我只是说可能只有一个这样的超级构造函数,因此 super() 语法是多余的。(我假设您知道在正常构造函数中如果省略 super 调用会发生什么...) - Stephen C

1
因为没有super()构造函数,参见dogbane的答案,并且因为编译器自动插入所需的super(...)调用,同样道理。
这是个问题的原因是什么?

我认为在构造函数中使用 super 是一个问题,因为这是编码标准/风格。虽然这被称为标准,但并不一定合理。 - IAdapter
@01:如果你的编码规范要求你调用不存在的父类构造函数,那么它肯定存在严重问题。更有可能的是你误解了需求。你必须调用super(x,y,...),其中x、y等是真实存在的构造函数参数。 - user207421

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接