3 .class files generated for 1 .java file

Q

qazmlp

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?
testClass$1.class
testClass$State.class
testClass.class

I expected only 1 class, 'testClass.class' to be generated. I do not understand
why other two classes(mainly testClass$1.class) are generated additionally.
Please clarify.

------testClass.java------
public class testClass
{
public static class State
{
private final String StateStr ;
private State(String StateStr ) { this.StateStr = StateStr ; }
public String toString() { return StateStr ; }
public final boolean equals(Object that)
{
return super.equals(that);
}
public final int hashCode()
{
return super.hashCode();
}
}

public static final State STATE_ONE = new State( "STATE_ONE" ) ;
public static final State STATE_TWO = new State( "STATE_TWO" ) ;
}
------testClass.java------
 
S

Sudsy

qazmlp said:
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?
testClass$1.class
testClass$State.class
testClass.class

I expected only 1 class, 'testClass.class' to be generated. I do not understand
why other two classes(mainly testClass$1.class) are generated additionally.
Please clarify.

------testClass.java------
public class testClass
{
public static class State

You've got an inner class here. That gives rise to the
testClass$State.class file. Note the name following the
$ sign: same as the name of your inner class. Why ARE
you trying to create the inner class in the first place?
 
D

Dave Glasser

The testClass$1.class file is indeed a mystery. That type of .class
file usually indicates the presence of an anonymous inner class, but
that's not the case here. I experimented and found that removing the
two static final State instances makes the testClass$1.class file go
away.

I think you've stumbled upon a compiler bug.


(e-mail address removed) (qazmlp) wrote on 18 Jan 2004 08:44:02 -0800
in comp.lang.java.programmer:
 
F

Filip Larsen

Dave Glasser wrote
The testClass$1.class file is indeed a mystery. That type of .class
file usually indicates the presence of an anonymous inner class, but
that's not the case here. I experimented and found that removing the
two static final State instances makes the testClass$1.class file go
away.

Changing the visibility of the State constructor from private to
protected also makes the anonymous class go away using 1.4.2 on win2k. I
also think this looks like a bug, because according to javap the class
simply extends from Object and I could delete it without getting any
NoClassDefFoundError from loading the top class.

The progressive compiler integral to Eclipse (2.1.2) do not produce any
such anonymous class in the first place.


Regards,
 
D

Dave Glasser

You've got an inner class here. That gives rise to the
testClass$State.class file. Note the name following the
$ sign: same as the name of your inner class. Why ARE
you trying to create the inner class in the first place?

What difference does that make?
 
T

Tony Morris

testClass$State.class
Inner class called "State"

testClass$1.class
Anonymous inner class.

--
Tony Morris
(BInfTech, Cert 3 I.T., SCJP[1.4], SCJD)
Software Engineer
IBM Australia - Tivoli Security Software
(2003 VTR1000F)
 
D

Dave Glasser

testClass$State.class
Inner class called "State"

testClass$1.class
Anonymous inner class.

Where's the code that's creating the anonymous inner class?
 
T

Tony Morris

The anonymous class is created is due to the fact that the outer class does
not have access to the inner class private constructor.

--
Tony Morris
(BInfTech, Cert 3 I.T., SCJP[1.4], SCJD)
Software Engineer
IBM Australia - Tivoli Security Software
(2003 VTR1000F)
 
D

Dave Glasser

The anonymous class is created is due to the fact that the outer class does
not have access to the inner class private constructor.

It's my understanding that an enclosing class has access to all
members of a nested static inner class, regardless of their access
modifiers, because the inner class is effectively part of the
enclosing class. My compiler agrees with me. But even if that's not
the case, I don't understand your statement above. If the outer class
does not have access to the inner class private constructor, how does
the creation of the anonymous inner class change anything?
 
J

Jon Skeet

Dave Glasser said:
It's my understanding that an enclosing class has access to all
members of a nested static inner class, regardless of their access
modifiers, because the inner class is effectively part of the
enclosing class. My compiler agrees with me. But even if that's not
the case, I don't understand your statement above. If the outer class
does not have access to the inner class private constructor, how does
the creation of the anonymous inner class change anything?

The enclosing class has access in terms of the *language*, but it
doesn't in terms of the *bytecode*. The compiler therefore mucks around
with things by adding a package-access constructor of TestClass$State
with signature TestClass$State(String, TestClass$1) and then using a
null reference as the second parameter. This extra constructor then
just calls the normal constructor.

For private methods, the compiler adds another method (eg "access$100")
which is static, package-access and takes an extra parameter which is
the State on which to call the method, and which just calls the method
appropriately.
 
C

Chris Uppal

Dave said:
But even if that's not
the case, I don't understand your statement above. If the outer class
does not have access to the inner class private constructor, how does
the creation of the anonymous inner class change anything?

See my answer to the OP.

-- chris
 
C

Chris Uppal

qazmlp said:
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);
}

class TestClass$1
{
}

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

(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.
 
C

Chris Uppal

Filip said:
The progressive compiler integral to Eclipse (2.1.2) do not produce any
such anonymous class in the first place.

That's because the eclipse compiler uses TestClass$Status as the "label" class
for the addition non-private synthetic constructor. Whereas Sun's compiler
creates an extra class for the same purpose.

-- chris
 
D

Dave Glasser

The enclosing class has access in terms of the *language*, but it
doesn't in terms of the *bytecode*. The compiler therefore mucks around
with things by adding a package-access constructor of TestClass$State
with signature TestClass$State(String, TestClass$1) and then using a
null reference as the second parameter. This extra constructor then
just calls the normal constructor.

For private methods, the compiler adds another method (eg "access$100")
which is static, package-access and takes an extra parameter which is
the State on which to call the method, and which just calls the method
appropriately.

Ah, I get it now. Thanks.

I wonder though if classes other than testClass in the same package
could instantiate State with:

testClass.State st = new testClass.State("STATE_ONE", null);

(I don't have time at the moment to experiment with it.)
 
K

Karl von Laudermann

I wrote the following code to get more type-safe C++-equivalent enum in Java.

As others have given you thorough answers to your actual question, I
won't reiterate what they've said. But I'd just like to point out that
Sun has a recommended pattern for typesafe enums, which you can find
at
http://developer.java.sun.com/developer/Books/shiftintojava/page1.html#replaceenums

It's similar to the way you've done it, but it doesn't use an inner
class. Also, supposedly Java 1.5 will include language support for
enums. There will basically be a simple syntax from which the compiler
generates the above pattern automatically.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,774
Messages
2,569,596
Members
45,140
Latest member
SweetcalmCBDreview
Top