> I wrote the following code to get more type-safe C++-equivalent enum in
> Java. Whenever I compile this code, 3 class files are generated. Why?
Expanding on the answers the others have already offered.
It is important to realise that Java is compiled *into* a high-level,
object-oriented, garbage collected, programming language called "JVM
bytecodes". Also known as classfiles.
That language is quite clean*, and is (I think) well worth looking into -- if
nothing else you will gain a better understanding of what Java can and can't do
(I'm not recommending that you learn to read/write JVM bytecodes themselves --
far too fiddly -- but it *is* worthwhile finding out what the semantics of the
JVM are).
In this case, the point is that while *Java* has nested classes, the *JVM* does
not. It never has had, and -- I expect -- never will have. So, the Java
compiler has to do some tricks to make it look as if those nested classes were
real (they are not, but the trick is quite good all the same). To compile
your code, the compiler "pretends" that it had been written:
================
public class TestClass
{
public static final State STATE_ONE
= new State("STATE_ONE", (TestClass$1)null);
public static final State STATE_TWO
= new State("STATE_TWO", (TestClass$1)null);
Quote:}
class TestClass$1
{
Quote:}
public class TestClass$State
{
private final String StateStr;
private TestClass$State(String StateStr) { this.StateStr = StateStr; }
TestClass$State(String StateStr, TestClass$1 x) { this(StateStr); }
public String toString() { return StateStr; }
public final boolean equals(Object that)
{
return super.equals(that);
}
public final int hashCode()
{
return super.hashCode();
}
Quote:}
================
(I've changed the class name to conform to the important convention that *all*
class names start in UPPER-CASE.)
You'll see that your nested class 'State' has been made into a real class in
its own right. Since the nested class was static, that's almost all the
compiler had to do (if it had been non-static, and especially if it had been an
inner class, then the compiler would have had to do more work). The only "odd"
thing is that, since you have declared the constructor for TestClass.State to
be private, that's what it has to be. So it is. But that gives the compiler a
problem, because the class initialiser for TestClass seems to call it. So what
the compiler does is create a "hidden" constructor that is not public, but
which is used only by TestClass. That constructor has to be different from the
one you provided, so the compiler creates a third class which is only ever used
as a "label" (no instances are ever created) for the additional constructor.
Since the extra constructor is package private, as is the label class itself,
this doesn't constitute an *enormous* hole in the Java security model, although
it's quite easy (unless you seal the jar file) for another programmer to access
the hidden constructor if he/she wants. But then, if you are worried about
such things, then you should also be aware that (for instance) the Sun 1.4.x
JVM does *not* enforce the member access rules at runtime unless you give it
the -future flag (it *should*, according to the spec, but it doesn't ;-).
-- chris
[*] cleaner than Java anyway.