I just hit very strange (to me) behaviour of java. I have following classes:
public abstract class Unit {
public static final Unit KM = KMUnit.INSTANCE;
public static final Unit METERS = MeterUnit.INSTANCE;
protected Unit() {
}
public abstract double getValueInUnit(double value, Unit unit);
protected abstract double getValueInMeters(double value);
}
And:
public class KMUnit extends Unit {
public static final Unit INSTANCE = new KMUnit();
private KMUnit() {
}
//here are abstract methods overriden
}
public class MeterUnit extends Unit {
public static final Unit INSTANCE = new MeterUnit();
private MeterUnit() {
}
///abstract methods overriden
}
And my test case:
public class TestMetricUnits extends TestCase {
@Test
public void testConversion() {
System.out.println("Unit.METERS: " + Unit.METERS);
System.out.println("Unit.KM: " + Unit.KM);
double meters = Unit.KM.getValueInUnit(102.11, Unit.METERS);
assertEquals(0.10211, meters, 0.00001);
}
}
I would expect that:
But when I run my test case in console with maven my result is:
T E S T S
Running de.audi.echargingstations.tests.TestMetricUnits<br/>
Unit.METERS: m<br/>
Unit.KM: null<br/>
Tests run: 3, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 0.089 sec <<< FAILURE! - in de.audi.echargingstations.tests.TestMetricUnits<br/>
testConversion(de.audi.echargingstations.tests.TestMetricUnits) Time elapsed: 0.011 sec <<< ERROR!<br/>
java.lang.NullPointerException: null<br/>
at <br/>de.audi.echargingstations.tests.TestMetricUnits.testConversion(TestMetricUnits.java:29)
<br/>
Results :
Tests in error:
TestMetricUnits.testConversion:29 NullPointer
And the funny part is that, when I run this test from eclipse via JUnit runner everything is fine, I have no NullPointerException
and in console I have:
Unit.METERS: m
Unit.KM: km
So the question is: what can be the reason that KM variable in Unit is null (and in the same time METERS is not null)
Static initialization can be tricky. You have an interdependency between A -> B and B -> A. The reason this is a bad idea is because of how the JVM starts loading statics from the top down in a class - if it hits a new class it has yet to initialize, it waits until it initializes that class, and its dependencies, recursively, until everything is ready, then proceeds.
Except when it's already loading a class. If A refers to B, and B refers to A, it can't start loading A a second time, or it would be an infinite loop (because A would load B again, which loads A). So, in that case, it basically says "already started loading that, nothing more to do, continuing on".
Moral of the story: depending on the ordering of the loading of classes, KMUnit.INSTANCE may not be initialized when you hit this line:
public static final Unit KM = KMUnit.INSTANCE;
Imagine that you're the JVM and you start loading KMUnit. It would have to load Unit, the first time it sees it, in order to, e.g. create a object that is a subclass of Unit when we get to the first creation (or perhaps before - I'm a touch foggy on JVM static loading). But that in turn triggers initialization of statics in Unit including this:
public static final Unit KM = KMUnit.INSTANCE;
public static final Unit METERS = MeterUnit.INSTANCE;
Ok. Now Unit is done loading, and we finish constructing a KMUnit for KMUnit.INSTANCE... but wait - we already set KM = KMUnit.INSTANCE
, which was null at the time. So it remains null. Oops.
On the other hand, if Unit loads first, then it waits for KMUnit to load before initializing, so KMUnit.INSTANCE is set when we actually run the initializer.
I think. I'm a bit sleep deprived, and I'm not an expert in classloading.
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