Testing some things out I tried o make an enum in which every one element in a enum have a different class inside.
Take for example:
public enum MyEnum {
first{
class First{}
},
second {
class Second{}
};
}
If i try to put a public modifier before any class, the Modifier not allowed here shows up. I am not quite sure why this would be. I cannot instantiate those classes outside the enum nor see them. However i can manage to get an Instance doing so:
public enum MyEnum {
first{
class First{}
public Object getObject(){
return new First();
}
},
second {
class Second{}
public Object getObject(){
return new Second();
}
};
public abstract Object getObject();
}
public class Main {
public static void main(String[] args) {
System.out.println(MyEnum.first.getObject().getClass());
System.out.println(MyEnum.second.getObject().getClass());
}
}
With the output:
class MyEnum$1$First
class MyEnum$2$Second
I can clearly have a reference to this class, why can't i access it then at compilation time?
JDK 16 changed the rules in that an inner class is now allowed to have static
members. It seems that the issue of this question has been fixed as a side effect. Since this should have been the behavior in earlier versions as well, public
is accepted by the JDK 16+ compiler even with --release 8
.
This is a very interesting question. You will not be able to access these classes at compile-time, even if the public
modifier was allowed, as they are contained within implicitly anonymous classes, so they can’t be accessed by name anyway (besides within the anonymous class). You can’t access a type through a variable, i.e. accessing MyEnum.first.First
is not possible in Java at all.
Still, not being useful is not necessarily determining what can be declared, i.e. declaring a public
inner class within a private
outer class is possible as well. The formal rules are relevant and while it looks like the expected behavior at the first glance, it is surprisingly not backed by the specification in that way.
JLS §8.9.1, Enum Constants states:
The optional class body of an enum constant implicitly defines an anonymous class declaration (§15.9.5) that extends the immediately enclosing enum type. The class body is governed by the usual rules of anonymous classes…
which gives us an interesting hint, i.e.
class Outer {
static Object o = new Object() {
public class Inner {
}
};
}
is rejected by the compiler either, prior to JDK 16.
Considering JLS, §8.1.1. Class Modifiers:
The access modifier
public
(§6.6) pertains only to top level classes (§7.6) and member classes (§8.5), not to local classes (§14.3) or anonymous classes (§15.9.5).
we have to decide in which category Inner
or your First
class fall. It’s not about the fact that their surrounding class is an anonymous class. Obviously, they are neither, top level nor anonymous classes, as they are nested and have a name. So they must be either, a member class (public
allowed) or a local class (public
not allowed).
JLS, §8.5. Member Type Declarations:
A member class is a class whose declaration is directly enclosed in the body of another class or interface declaration (§8.1.6, §9.1.4).
The “body of another class … declaration” is defined by pointing to §8.1.6 which indeed defines the ClassBody
language grammar, which is commonly used by named declarations, anonymous classes and enum
constant bodies; all of them point to the “class body” of §8.1.6. Considering this, our classes are “member classes” as they are contained in a class body.
Now we could try to interpret that as a wrong cross reference, assuming that “body of another class … declaration” was meant to refer to Class Declarations, i.e. named class declarations using the class
keyword, however, there is the definition of local classes which disproves this interpretation.
JLS §14.3, Local Classes:
A local class is a nested class (§8 (Classes)) that is not a member of any class and that has a name (§6.2, §6.7).
…
Every local class declaration statement is immediately contained by a block (§14.2). Local class declaration statements may be intermixed freely with other kinds of statements in the block.
“Block” really means the block like the definition of a non-abstract
method, constructor or initializer (§14.2). This does not apply to the Inner
class above or your First
and Second
class. They are not placed in a block and may not be intermixed freely with statements in that context, as statement aren’t allowed at this point.
In other words, they are definitely not local classes and assuming that there is not another category of classes not described by the specification, we have to consider them member classes, as the current writing and linkage of the definition of member classes also suggest, in other words, according to the cited rules, the public
modifier should be allowed at this place.
For completeness, here is the definition of anonymous classes, just to show that there is no exceptional rule saying that their member classes aren’t allowed to be public
:
15.9.5. Anonymous Class Declarations
An anonymous class declaration is automatically derived from a class instance creation expression by the Java compiler.
An anonymous class is never
abstract
(§8.1.1.1).An anonymous class is always implicitly
final
(§8.1.1.2).An anonymous class is always an inner class (§8.1.3); it is never
static
(§8.1.1, §8.5.1).
The last point implies that in turn, their member classes can’t be static
either, however, there is no rule forbidding them to be public
.
You are subclassing MyEnum
with your current approach (the {}
syntax creates an anonymous subclass), which works but seems overly complicated. What you have is equivalent to something like,
public enum MyEnum {
first, second; // <-- convention would be FIRST, SECOND
public static class First { // <-- can be public.
}
public static class Second {
}
public Object getObject() {
if (this == first) {
return new First();
}
return new Second();
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With