I have a complicated problem in a Java system. Here is a glimpse at it:
public class Foo {
public static final Foo FOOBAR = Bar.VALUE;
...
}
public class Bar extends Foo {
public static final Bar VALUE = new Bar(...);
...
}
In this case, I guess it would be obvious that there is a cyclic static dependency and there are moments when one will access Foo.FOOBAR or Bar.VALUE to find it is null. Class Bar depends on class Foo having been initialized, but to complete that, it needs an instance of class Bar.
Despite the cyclical static initialization being obviously problematic, I have been using this system for many years and only recently some special cases came up in which things fail. I have tried to isolate the problem and it's strange behavior, but have not been able to get out of trivial cases. I have not been able to isolate the issue clearly.
So now I am coming to you with a general question: under what condition can it be possible -- and why does the Java compiler or JRE not identify such incomplete cyclic dependency?
I did this to avoid some instances of the problem in the real running system:
public class Foo {
public static final Foo FOOBAR = new Bar(...);
...
}
public class Bar extends Foo {
public static final Bar VALUE = Foo.FOOBAR;
...
}
and this seemed to help in those cases, but apparently not in others. I.e., Bar.VALUE remains null. Why would it work any "better" though? Here we need to initialize class Foo before class Bar, but again Foo depends on Bar to be initialized.
I am surprised that I don't get a error or warning about this. Neither at compile-time nor at runtime. Or am I not looking closely enough?
How can I completely resolve this conundrum? I want the Foo.FOOBAR and the Bar.VALUE to be identical, and I want both to be finals. Is there any way I can defer the initialization of static finals in a way that the dependence will be initialized first?
I don't believe that there is a principle logical problem why this must be avoided altogether. To explain, a related example, in SQL databases, the null value(s) are actually typed. So you have a NULL number different from a NULL varchar. The use case here is a Null class (implementing a NULL interface) and an Number class, implementing an Number interface.) NullNumber, is a specialization of Null which happens to be a Number type. We also want to have singleton values, so there would be only one instance for an NullNumber. And it is as valid to ask "hey Number class, give me your NULL value" as it is to ask "hey Null class, give me your Number version".