Tuesday, 18 September 2012

Puzzle 49: Larger Than Life


Lest you think that this book is going entirely to the dogs, this puzzle concerns royalty. If the tabloids are to be believed, the King of Rock 'n' Roll is still alive. Not one of his many impersonators but the one true Elvis. This program estimates his current belt size by projecting the trend observed during his public performances. The program uses the idiom Calendar.getInstance().get(Calendar.YEAR), which returns the current calendar year. What does the program print?
public class Elvis {

    public static final Elvis INSTANCE = new Elvis();

    private final int beltSize;

    private static final int CURRENT_YEAR =

        Calendar.getInstance().get(Calendar.YEAR);



    private Elvis() {

        beltSize = CURRENT_YEAR - 1930;

    }



    public int beltSize() {

        return beltSize;

    }



    public static void main(String[] args) {

        System.out.println("Elvis wears a size " +

                           INSTANCE.beltSize() + " belt.");

    }

}


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, but they may result in constructor invocation before static fields are initialized. Static fields, even final static fields, may be observed with their default value before they are initialized.

No comments:

Post a Comment

Your comments are welcome!