Constructors in Subclasses
As we explained in the preceding section, instantiating a subclass object begins a chain of constructor calls in which the subclass constructor, before performing its own tasks, invokes its direct superclass's constructor either explicitly (via the super reference) or implicitly (calling the superclass's default constructor or no-argument constructor). Similarly, if the superclass is derived from another class (as is, of course, every class except Object), the superclass constructor invokes the constructor of the next class up in the hierarchy, and so on. The last constructor called in the chain is always the constructor for class Object. The original subclass constructor's body finishes executing last. Each superclass's constructor manipulates the superclass instance variables that the subclass object inherits. For example, consider again the CommissionEmployee3BasePlusCommissionEmployee4 hierarchy from Fig. 9.12 and Fig. 9.13. When a program creates a BasePlusCommissionEmployee4 object, the BasePlusCommissionEmployee4 constructor is called. That constructor calls CommissionEmployee3's constructor, which in turn calls Object's constructor. Class Object's constructor has an empty body, so it immediately returns control to CommissionEmployee3's constructor, which then initializes the private instance variables of CommissionEmployee3 that are part of the BasePlusCommissionEmployee4 object. When CommissionEmployee3's constructor completes execution, it returns control to BasePlusCommissionEmployee4's constructor, which initializes the BasePlusCommissionEmployee4 object's baseSalary.
Software Engineering Observation 9.8
When a program creates a subclass object, the subclass constructor immediately calls the superclass constructor (explicitly, via super, or implicitly). The superclass constructor's body executes to initialize the superclass's instance variables that are part of the subclass object, then the subclass constructor's body executes to initialize the subclass-only instance variables. Java ensures that even if a constructor does not assign a value to an instance variable, the variable is still initialized to its default value (e.g., 0 for primitive numeric types, false for booleans, null for references). |
Our next example revisits the commission employee hierarchy by declaring a CommissionEmployee4 class (Fig. 9.15) and a BasePlusCommissionEmployee5 class (Fig. 9.16). Each class's constructor prints a message when invoked, enabling us to observe the order in which the constructors in the hierarchy execute.
Figure 9.15. CommissionEmployee4's constructor outputs text.
(This item is displayed on pages 445 - 447 in the print version)
1 // Fig. 9.15: CommissionEmployee4.java 2 // CommissionEmployee4 class represents a commission employee. 3 4 public class CommissionEmployee4 5 { 6 private String firstName; 7 private String lastName; 8 private String socialSecurityNumber; 9 private double grossSales; // gross weekly sales 10 private double commissionRate; // commission percentage 11 12 // five-argument constructor 13 public CommissionEmployee4( String first, String last, String ssn, 14 double sales, double rate ) 15 { 16 // implicit call to Object constructor occurs here 17 firstName = first; 18 lastName = last; 19 socialSecurityNumber = ssn; 20 setGrossSales( sales ); // validate and store gross sales 21 setCommissionRate( rate ); // validate and store commission rate 22 23 System.out.printf( 24 " CommissionEmployee4 constructor: %s ", this ); 25 } // end five-argument CommissionEmployee4 constructor 26 27 // set first name 28 public void setFirstName( String first ) 29 { 30 firstName = first; 31 } // end method setFirstName 32 33 // return first name 34 public String getFirstName() 35 { 36 return firstName; 37 } // end method getFirstName 38 39 // set last name 40 public void setLastName( String last ) 41 { 42 lastName = last; 43 } // end method setLastName 44 45 // return last name 46 public String getLastName() 47 { 48 return lastName; 49 } // end method getLastName 50 51 // set social security number 52 public void setSocialSecurityNumber( String ssn ) 53 { 54 socialSecurityNumber = ssn; // should validate 55 } // end method setSocialSecurityNumber 56 57 // return social security number 58 public String getSocialSecurityNumber() 59 { 60 return socialSecurityNumber; 61 } // end method getSocialSecurityNumber 62 63 // set gross sales amount 64 public void setGrossSales( double sales ) 65 { 66 grossSales = ( sales < 0.0 ) ? 0.0 : sales; 67 } // end method setGrossSales 68 69 // return gross sales amount 70 public double getGrossSales() 71 { 72 return grossSales; 73 } // end method getGrossSales 74 75 // set commission rate 76 public void setCommissionRate( double rate ) 77 { 78 commissionRate = ( rate > 0.0 && rate < 1.0 ) ? rate : 0.0; 79 } // end method setCommissionRate 80 81 // return commission rate 82 public double getCommissionRate() 83 { 84 return commissionRate; 85 } // end method getCommissionRate 86 87 // calculate earnings 88 public double earnings() 89 { 90 return getCommissionRate() * getGrossSales(); 91 } // end method earnings 92 93 // return String representation of CommissionEmployee4 object 94 public String toString() 95 { 96 return String.format( "%s: %s %s %s: %s %s: %.2f %s: %.2f", 97 "commission employee", getFirstName(), getLastName(), 98 "social security number", getSocialSecurityNumber(), 99 "gross sales", getGrossSales(), 100 "commission rate", getCommissionRate() ); 101 } // end method toString 102 } // end class CommissionEmployee4 |
Figure 9.16. BasePlusCommissionEmployee5's constructor outputs text.
(This item is displayed on pages 447 - 448 in the print version)
1 // Fig. 9.16: BasePlusCommissionEmployee5.java 2 // BasePlusCommissionEmployee5 class declaration. 3 4 public class BasePlusCommissionEmployee5 extends CommissionEmployee4 5 { 6 private double baseSalary; // base salary per week 7 8 // six-argument constructor 9 public BasePlusCommissionEmployee5( String first, String last, 10 String ssn, double sales, double rate, double salary ) 11 { 12 super( first, last, ssn, sales, rate ); 13 setBaseSalary( salary ); // validate and store base salary 14 15 System.out.printf( 16 " BasePlusCommissionEmployee5 constructor: %s ", this ); 17 } // end six-argument BasePlusCommissionEmployee5 constructor 18 19 // set base salary 20 public void setBaseSalary( double salary ) 21 { 22 baseSalary = ( salary < 0.0 ) ? 0.0 : salary; 23 } // end method setBaseSalary 24 25 // return base salary 26 public double getBaseSalary() 27 { 28 return baseSalary; 29 } // end method getBaseSalary 30 31 // calculate earnings 32 public double earnings() 33 { 34 return getBaseSalary() + super.earnings(); 35 } // end method earnings 36 37 // return String representation of BasePlusCommissionEmployee5 38 public String toString() 39 { 40 return String.format( "%s %s %s: %.2f", "base-salaried", 41 super.toString(), "base salary", getBaseSalary() ); 42 } // end method toString 43 } // end class BasePlusCommissionEmployee5 |
Class CommissionEmployee4 (Fig. 9.15) contains the same features as the version of the class shown in Fig. 9.4. We modified the constructor (lines 1325) to output text upon its invocation. Note that outputting this with the %s format specifier (lines 2324) implicitly invokes the toString method of the object being constructed to obtain the object's string representation.
Class BasePlusCommissionEmployee5 (Fig. 9.16) is almost identical to BasePlusCommissionEmployee4 (Fig. 9.13), except that BasePlusCommissionEmployee5's constructor also outputs text when invoked. As in CommissionEmployee4 (Fig. 9.15), we output this using the %s format specifier in line 16 to obtain the object's string representation.
Figure 9.17 demonstrates the order in which constructors are called for objects of classes that are part of an inheritance hierarchy. Method main begins by instantiating CommissionEmployee4 object employee1 (lines 89). Next, lines 1214 instantiate BasePlusCommissionEmployee5 object employee2. This invokes the CommissionEmployee4 constructor, which prints output with the values passed from the BasePlusCommissionEmployee5 constructor, then performs the output specified in the BasePlusCommissionEmployee5 constructor. Lines 1719 then instantiate BasePlusCommissionEmployee5 object employee3. Again, the CommissionEmployee4 and BasePlusCommissionEmployee5 constructors are both called. In each case, the body of the CommissionEmployee4 constructor executes before the body of the BasePlusCommissionEmployee5 constructor executes. Note that employee2 is constructed completely before construction of employee3 begins.
Figure 9.17. Constructor call order.
(This item is displayed on pages 448 - 449 in the print version)
1 // Fig. 9.17: ConstructorTest.java 2 // Display order in which superclass and subclass constructors are called. 3 4 public class ConstructorTest 5 { 6 public static void main( String args[] ) 7 { 8 CommissionEmployee4 employee1 = new CommissionEmployee4( 9 "Bob", "Lewis", "333-33-3333", 5000, .04 ); 10 11 System.out.println(); 12 BasePlusCommissionEmployee5 employee2 = 13 new BasePlusCommissionEmployee5( 14 "Lisa", "Jones", "555-55-5555", 2000, .06, 800 ); 15 16 System.out.println(); 17 BasePlusCommissionEmployee5 employee3 = 18 new BasePlusCommissionEmployee5( 19 "Mark", "Sands", "888-88-8888", 8000, .15, 2000 ); 20 } // end main 21 } // end class ConstructorTest
|