Skip to main content

Bitten by the class literal change in Tiger

Posted by mister__m on August 16, 2006 at 10:02 PM PDT

If you are a returning reader, you're probably aware of the enum implementation I wrote for Java 1.4 almost three years ago. Running some Java 1.4 compatible code compiled with Java 5 has just called my attention to a supposedly low impact change that was implemented in Tiger.

Since Java 5, a class literal, i.e., an expression such as MyClass.class does not trigger class initialization anymore. This is a well known, documented issue, but solutions are not clean nor cover all previously supported scenarios.

Let me explain how my enum implementation is affected by this bug so you can understand if this bug might affect your Java 1.4 code. Enum's sole constructor registers the newly created instance's in a few internal Map cache structures. I mean, when something like this is executed:

public final class MyEnum extends Enum {
   public static final MyEnum A = new MyEnum("A");
   public static final MyEnum B = new MyEnum("B");

   private MyEnum(final String name) {

, cache is populated to tell what is the domain for MyEnum (A and B) and to be able to tell you that the instance that represents the String constant "A" in runtime for MyEnum is MyEnum.A

Ok, now that we have a real world example, let's see what worked with Java 1.4 and fails with Tiger and Mustang:

MyEnum m = (MyEnum)Enum.get(MyEnum.class, typedValue);

With Java 1.4, it works; with Java 5, you will get null. The reason is simple: A and B are never instantiated because the class is never initialized.

Now, to the shocking news: there is no way to tell whether a Class instance has been initialized nor a 100% reliable way to force it to initialize. Workarounds such as:

Class c = MyEnum.class;
Class.forName(c.getName(), true, c.getClassLoader());

will require you to handle an exception that should never been thrown in this situation (ClassNotFoundException) and will fail if c.getClassLoader() == null and your code haven't been granted the RuntimePermission("getClassLoader") permission.

Therefore, I've submitted a RFE to add Class.initialize() and Class.isInitialized() to Java SE. I really would like to see this implemented for Java SE 6, but I think it will have to wait for Java 7. This change involves messing with shared native C code and refactoring a lot of stuff on the way, so you are welcome to help me in this task in the JDK Collaboration project.

For now, beware of code that relies on class literal triggering class initialization and hope the ugly workaround above works for you if you cannot avoid it.

Related Topics >>