是什么让java中的枚举不可实例化?

时间:2016-04-19 05:56:53

标签: java constructor enums instantiation

我知道这是一个枚举

enum Year
{
   First, Second, Third, Fourth;
}

转换为

final class Year extends Enum<Year>
{
        public static final Year First = new Year();
        public static final Year Second = new Year();
        public static final Year Third = new Year();
        public static final Year Fourth = new Year();
}

当我尝试实例化枚举(不是类)时,我得到编译时错误:

error: enum types may not be instantiated
        Year y = new Year();

据我所知,私有构造函数使类不可实例化。我认为编译器提供了一个私有构造函数。但是当我看到我们可以使用默认修饰符为枚举定义构造函数时,我又感到困惑,但仍然无法创建枚举类型的对象。

enum Year
{
        First, Second, Third, Fourth;
        Year()
        {
        }
}

class Example
{
        public static void main(String[] args)
        {
                Year y = new Year();
        }
}

我的疑问是,如果它不是关于构造函数那么是什么让Java中的枚举不可实现?

2 个答案:

答案 0 :(得分:20)

Java Language Specification

中指定
  

8.9. Enum Types

     

...

     

枚举类型没有除其枚举常量定义的实例之外的实例。尝试显式实例化枚举类型(第15.9.1节)是编译时错误。

因此编译器可确保满足此要求。由于编译器“知道”该类型是enum,因此它可以区分enum Yearfinal class Year

此外,枚举构造函数不允许使用访问修饰符:

  

8.9.2. Enum Body Declarations

     

...

     

如果枚举声明中的构造函数声明是公共的或受保护的,则是编译时错误。

     

...

     

在枚举声明中,没有访问修饰符的构造函数声明是私有的

因此,在实践中,enum构造函数看起来像包作用域(无访问修饰符),但它确实是私有的。

最后,同一部分还说明了

  

在没有构造函数声明的枚举声明中,隐式声明了默认构造函数。 默认构造函数是私有,没有正式参数,并且没有throws子句。

即使没有显式声明构造函数,这也使得enum不可实例化。

答案 1 :(得分:6)

java中的enum有一个默认的构造函数,当它没有被定义时,它是私有的。

默认访问修饰符在不同范围内具有不同的含义。例如,在方法和字段的类默认访问修饰符中,package private

在界面中,默认访问修饰符的含义为public。事实上,接口字段上不能有其他修饰符,因此它是隐式公开的。

在顶级类中,它是私有的包(其中只允许2个访问修饰符public和默认包私有)

所以问题的答案就是这样,因为编译器决定这样做。编译器编写者必须坚持语言规范合同。

你认为这是一切正常的课程之后你是正确的。 java中的每个对象蓝图类型都是一个可以用java.lang.Class表示的类。接口,枚举,抽象类,匿名类,方法本地类的这些限制仅由编译器验证。

如果您可以以某种方式逃避编译器并为枚举生成自己的字节代码或其他方式,如果您可以修改生成的enum类的字节代码,以便它的私有构造函数变为公共可能您将能够在枚举的私有范围之外调用它的构造函数。您也可以尝试使用反射来做同样的事情。事实上,通过手动生成字节代码,Groovy,Jython,JRuby,Clojure等JVM语言能够提供非Java本身的功能。他们绕过java编译器。

enum中拥有构造函数的目的是能够在一次调用中设置常量字段。 enum中的所有常量都是enum类的实例,因此它们也包含在其中声明的字段。

enum Test
{
    T1(1), // equivalent to public static final Test T1 = new Test(1);
    T2(2); // equivalent to public static final Test T2 = new Test(2);

    int id;
    Test(int id)
    {
        this.id = id;
    }
}

最后,下面是使用enum

的上述java -p Test.class的反编译代码的输出
final class Test extends java.lang.Enum<Test>
{
    public static final Test T1;
    public static final Test T2;
    int id;
    private static final Test[] $VALUES;
    public static Test[] values();
    public static Test valueOf(java.lang.String);
    private Test(int);
    static {};
}

它应该更好地理解类编译时会发生什么。