Thursday, February 25, 2010

Static Blocks and Static Inheritence.

I was using a library that made heavy use of the static inheritance behavior of Java.

This example is rather contrived, but stick with me. So imagine this situation, You have a super class named Base:
public class Base {
public static String searchEngine = "http://www.google.com";
public static void search() { System.out.println(searchEngine); }
}
public class Child {
static {
searchEngine = "http://www.bing.com";
}
}

Given the above classes, execute the following program code:
Base.search();
Child.search();

Some of you noted that I called a static method on Child that does not exist. Technically that is true. The Java compiler understands that Child inherited Base.search() and properly compiles. Nice feature. Especially helpful when you are doing static imports and what all of the inherited static methods as well.

So here is the resulting output:
http://www.google.com
http://www.google.com

This is where things get interesting. While the compiler understands the inheritance tree, the Java Runtime doesn't. So for the purposes of bytecode generation, the call to "Child.search()" gets replaced with a call to "Base.search()". That is fine. Totally acceptable behavior. A lot of constructs in java are syntactical sugar for more elaborate byte-code.

But now we get to the part I consider a bug, or at least an ambiguous behavior. For "Child.search()", I obviously referenced the Child class. As such, I expected the Child static block to be executed. But the resulting bytecode contains a reference to Base; the class loader has no reason to load the class Child in that example thus the Child static blokc is not executed.

Now, there are a couple of ways to force the Class loader to do what I want:

Class.forName("Child"); Child.search(); //jdbc example, force reference before.
new Child(); Child.search(); //hard coded example, same as above.
new Child().search(); // Unholy. don't call static methods against instances.

None of these are optimal solutions. I especially think the last one is unholy. Back to my problem, I am going to redesign the library in question to remove the super class dependency on a child static block being executed. I don't like telling the JVM or the class loader what to do based on hints, and I think depending on the order of static block execution is equivalent to not vaccinating your kids for Polio -- sure, things might just work out fine, but do you really want to risk them getting Polio?