What makes enum in java non instantiable?

那年仲夏 提交于 2020-06-25 07:36:10

问题


I know that an enum

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

gets converted into

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();
}

When I tried to instantiate enum (not class) I got compile time error as:

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

As per my knowledge a private constructor makes a class non instantiable. And I thought that compiler is providing a private constructor. But again I got confused when saw we can define a constructor for enum with default modifier and still cannot create an object of type enum.

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

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

My doubt is, if it is not about constructors then what makes enum in Java non instantiable?


回答1:


It is specified in the Java Language Specification:

8.9. Enum Types

...

An enum type has no instances other than those defined by its enum constants. It is a compile-time error to attempt to explicitly instantiate an enum type (§15.9.1).

Hence the compiler ensures that this requirement is met. Since the compiler "knows" that the type is an enum, it can distinguish between enum Year and final class Year.

Also, no access modifier is allowed for an enum constructor:

8.9.2. Enum Body Declarations

...

It is a compile-time error if a constructor declaration in an enum declaration is public or protected.

...

In an enum declaration, a constructor declaration with no access modifiers is private.

So, in practice, an enum constructor looks like package-scoped (no access modifier), but it really is private.

Finally, the same section also states

In an enum declaration with no constructor declarations, a default constructor is implicitly declared. The default constructor is private, has no formal parameters, and has no throws clause.

This makes the enum non-instantiable even if no constructor is explicitly declared.




回答2:


An enum in java has a default constructor when it's is not defined and it's private.

Default access modifier has different meanings in different scopes. For example inside a class default access modifier for methods and fields is package private.

Where as in an interface default access modifier means public. In fact there can be no other modifier on an interface field so it's implicitly public.

On a top level class it's package private (where only 2 access modifiers are allowed public and default package private)

So the answer to your question is it is so because compiler decides so. Compiler writer had to uphold the language specification contract.

You're right in thinking that it's a normal class after everything. Every object blueprint type in java is a class which can be represented by java.lang.Class. These restrictions for interfaces, enums, abstract classes, anonymous classes, method local classes are validated by compiler only.

If you can somehow escape the compiler and generate your own byte code for enums or other way around if you can modify the byte code of generated enum class so that it's private constructor becomes public may be you would be able to call it's constructor outside the enum's private scope. You can also try experimenting with reflection to do the same. In fact by generating byte code manually JVM languages like Groovy, Jython, JRuby, Clojure are able to provide functionalities that are not in Java itself. They're bypassing java compiler.

Purpose for having constructor in enums is to be able to set fields of constants in one call. All constants inside an enum are instances of the enum class, so they also consist the fields declared in it.

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;
    }
}

And finally bellow is the output of decompiled code for above enum by using 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 {};
}

It should give a better understanding of what happens when the class compiles.



来源:https://stackoverflow.com/questions/36709719/what-makes-enum-in-java-non-instantiable

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!