Java Reference
In-Depth Information
protected String makeName() {
// 4. Executes before subclass constructor body!
return super.makeName() + ":" + color;
}
public static void main(String[] args) {
// 1. Invoke subclass constructor
System.out.println(new ColorPoint(4, 2, "purple"));
}
}
In the explanation that follows, the numbers in parentheses refer to the numbers in the comments in
the annotated listing. First, the program creates a ColorPoint instance by invoking the ColorPoint
constructor (1). This constructor starts by chaining to the superclass constructor, as all constructors
do (2). The superclass constructor assigns 4 to the x field of the object under construction and 2 to
its y field. Then the constructor invokes makeName , which is overridden by the subclass (3).
The makeName method in ColorPoint (4) executes before the body of the ColorPoint constructor,
and therein lies the heart of the problem. The makeName method first invokes super.makeName ,
which returns [4,2] as expected. Then the method appends the string ":" and the value of the
color field, converted to a string. But what is the value of the color field at this point? It has yet to
be initialized, so it still contains its default value of null . Therefore, the makeName method returns
the string "[4,2]:null" . The superclass constructor assigns this value to the name field (3) and
returns control to the subclass constructor.
The subclass constructor then assigns the value "purple" to the color field (5), but it is too late.
The color field has already been used to initialize the name field in the superclass to an incorrect
value. The subclass constructor returns, and the newly created ColorPoint instance is passed to the
println method, which duly invokes its toString method. This method returns the contents of its
name field, "[4,2]:null" , so that is what the program prints.
This puzzle illustrates that it is possible to observe the value of a final instance field before its
value has been assigned , when it still contains the default value for its type. In a sense, this puzzle
is the instance analog of Puzzle 49 , which observed the value of a final static field before its value
had been assigned. In both cases, the puzzle resulted from a circularity in initialization. In Puzzle
49 , it was class initialization; in this puzzle, it is instance initialization. Both cases have the
potential for enormous confusion. There is one point where the analogy breaks down: Circular class
initialization is a necessary evil, but circular instance initialization can and should always be
avoided.
 
 
Search WWH ::




Custom Search