问题
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 enum
s 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