Java Reference
In-Depth Information
Solution 49: Larger Than Life
At first glance, this program appears to compute the current year minus 1930. If that were correct,
in the year 2006, the program would print Elvis wears a size 76 belt . If you tried running the
program, you learned that the tabloids were wrong, proving that you can't believe everything you
read in the papers. It prints Elvis wears a size -1930 belt . Perhaps the King has gone on to
inhabit an anti-matter universe?
This program suffers a problem caused by a circularity in the order of class initialization [JLS
12.4]. Let's follow it in detail. Initialization of the class Elvis is triggered by the VM's call to its
main method. First, static fields are set to their default values [JLS 4.12.5]. The field INSTANCE is
set to null , and CURRENT_YEAR is set to 0 . Next, static field initializers are executed in order of
appearance. The first static field is INSTANCE . Its value is computed by invoking the Elvis()
constructor.
The constructor initializes beltSize to an expression involving the static field CURRENT_YEAR .
Normally, reading a static field is one of the things that causes a class to be initialized, but we are
already initializing the class Elvis . Recursive initialization attempts are simply ignored [JLS
12.4.2, step 3]. Consequently, the value of CURRENT_YEAR still has its default value of 0. That is why
Elvis's belt size turns out to be -1930 .
Finally, returning from the constructor to complete the class initialization of Elvis , we initialize the
static field CURRENT_YEAR to 2006, assuming you're running the program in 2006. Unfortunately, it
is too late for the now correct value of this field to affect the computation of
Elvis.INSTANCE.beltSize , which already has the value -1930 . This is the value that will be
returned by all subsequent calls to Elvis.INSTANCE.beltSize ().
This program shows that it is possible to observe a final static field before it is initialized , when
it still contains the default value for its type. That is counterintuitive, because we usually think of
final fields as constants. Final fields are constants only if the initializing expression is a constant
expression [JLS 15.28].
Problems arising from cycles in class initialization are difficult to diagnose but once diagnosed are
usually easy to fix. To fix a class initialization cycle, reorder the static field initializers so that
each initializer appears before any initializers that depend on it. In this program, the declaration
for CURRENT_YEAR belongs before the declaration for INSTANCE , because the creation of an Elvis
instance requires that CURRENT_YEAR be initialized. Once the declaration for CURRENT_YEAR has been
moved, Elvis will indeed be larger than life.
Some common design patterns are naturally subject to initialization cycles, notably the Singleton
[Gamma95] , which is illustrated in this puzzle, and the Service Provider Framework [EJ Item 1].
The Typesafe Enum pattern [EJ Item 21] also causes class initialization cycles. Release 5.0 adds
linguistic support for this pattern with enum types. To reduce the likelihood of problems, there are
some restrictions on static initializers in enum types [JLS 16.5, 8.9].
In summary, be careful of class initialization cycles. The simplest ones involve only a single class,
but they can also involve multiple classes. It isn't always wrong to have class initialization cycles,
 
 
Search WWH ::




Custom Search