Practical Java Praxis 68: Use Care When Calling Non-final Methods from Constructors
A constructor for a class is invoked when a new instance of that class is created. The purpose of a constructor is to initialize an object. When the code in a constructor executes, it can call methods of its class. Constructors are often coded to do this because methods of the class might contain initialization code. For example, take a look at this code:
class Base { private int val; Base() { val = lookup(); } public int lookup() { //Perform a database lookup int num = dbLookup(); //return num; return 5; } public int value() { return val; } }
The constructor for class Base calls a non-final method, lookup, to retrieve some data from a database. This code works as expected, with the instance data val, of class Base, being assigned the value 5. (Actually, the lookup method returns the value from the dbLookup method. The value 5 is returned for simplicity of illustration.)
Consider what happens if a derived class overrides the lookup method of class Base. Depending on how the overridden method is coded, this can lead to nonintuitive results. For example, look at this code :
class Base { private int val; Base() { val = lookup(); } public int lookup() { //Perform a database lookup int num = dbLookup(); //return num; return 5; } public int value() { return val; } } class Derived extends Base { private int num = 10; public int lookup() { return num; } } class Test { public static void main(String args[]) { Derived d = new Derived(); System.out.println("From main() d.value() returns " + d.value()); } }
The output of this code is as follows:
From main() d.value() returns 0
The problem is that the method lookup of class Derived returned the value 0. You might wonder how this could happen, given the method's implementation. The method returns the value of the instance variable num, which is assigned the value 10 during instance variable initialization. However, instance variable initialization for class Derived has not occurred at the time its lookup method executes.
The lookup method is called from the constructor of class Base during the construction of an object of class Derived. When the lookup method of class Derived is entered, its instance variable initialization has not been performed. Its instance variables have only been set to their default initial values. In this case, val has been set to the value 0. Thus, 0 is returned from this method. This error can occur when a constructor calls a non-final method of its class. A problem results if this method is overridden by a derived class and that method returns a value that was initialized during instance variable initialization. This type of error might not be that common. However, knowing that it exists can save a lot of time when you encounter it.
About the Author
Peter Haggar is a senior software engineer with IBM in Research Triangle Park, North Carolina, and author of the best-selling book Practical Java, published by Addison-Wesley. Having worked on development tools, class libraries, and operating systems, he has a broad range of programming experience. At IBM, Peter works on emerging Java technology and, most recently, on real-time Java. He is also a frequent technical speaker on Java technology at numerous industry conferences. Peter received his bachelor of science degree in computer science from Clarkson University. He can be contacted at haggar@us.ibm.com.