G.4.4. CommissionEmployeeBasePlusCommissionEmployee Inheritance Hierarchy Using protected Instance Variables

To enable class BasePlusCommissionEmployee to directly access superclass instance variables firstName, lastName, socialSecurityNumber, grossSales and commissionRate, we can declare those members as protected in the superclass. As we discussed in Section G.3, a superclass’s protected members are accessible by all subclasses of that superclass. In the new CommissionEmployee class, we modified only lines 6–10 of Fig. G.4 to declare the instance variables with the protected access modifier as follows:

protected String firstName;
protected String lastName;
protected String socialSecurityNumber;
protected double grossSales; // gross weekly sales
protected double commissionRate; // commission percentage

The rest of the class declaration (which is not shown here) is identical to that of Fig. G.4.

We could have declared CommissionEmployee’s instance variables public to enable subclass BasePlusCommissionEmployee to access them. However, declaring public instance variables is poor software engineering because it allows unrestricted access to the these variables, greatly increasing the chance of errors. With protected instance variables, the subclass gets access to the instance variables, but classes that are not subclasses and classes that are not in the same package cannot access these variables directly—recall that protected class members are also visible to other classes in the same package.

Class BasePlusCommissionEmployee

Class BasePlusCommissionEmployee (Fig. G.9) extends the new version of class CommissionEmployee with protected instance variables. BasePlusCommissionEmployee objects inherit CommissionEmployee’s protected instance variables firstName, lastName, socialSecurityNumber, grossSales and commissionRate—all these variables are now protected members of BasePlusCommissionEmployee. As a result, the compiler does not generate errors when compiling line 37 of method earnings and lines 46–48 of method toString. If another class extends this version of class BasePlusCommissionEmployee, the new subclass also can access the protected members.


 1      // Fig. G.9: BasePlusCommissionEmployee.java
 2      // BasePlusCommissionEmployee inherits protected instance
 3      // variables from CommissionEmployee.
 4
 5      public class BasePlusCommissionEmployee extends CommissionEmployee
 6      {
 7         private double baseSalary; // base salary per week
 8
 9         // six-argument constructor
10         public BasePlusCommissionEmployee( String first, String last,
11            String ssn, double sales, double rate, double salary )
12         {
13            super ( first, last, ssn, sales, rate );
14            setBaseSalary( salary ); // validate and store base salary
15         } // end six-argument BasePlusCommissionEmployee constructor
16
17         // set base salary
18         public void setBaseSalary( double salary )
19         {
20            if ( salary >= 0.0 )
21               baseSalary = salary;
22            else
23               throw new IllegalArgumentException(
24                  "Base salary must be >= 0.0" );
25         } // end method setBaseSalary
26
27         // return base salary
28         public double getBaseSalary()
29         {
30            return baseSalary;
31         } // end method getBaseSalary
32
33         // calculate earnings
34         @Override // indicates that this method overrides a superclass method
35         public double earnings()
36         {
37            return baseSalary + ( commissionRate * grossSales );
38         } // end method earnings
39
40         // return String representation of BasePlusCommissionEmployee
41         @Override // indicates that this method overrides a superclass method
42         public String toString()
43         {
44            return String.format(                                           
45               "%s: %s %s %s: %s %s: %.2f %s: %.2f %s: %.2f",           
46               "base-salaried commission employee", firstName, lastName,    
47               "social security number", socialSecurityNumber,              
48               "gross sales", grossSales, "commission rate", commissionRate,
49               "base salary", baseSalary );                                 
50         } // end method toString
51      } // end class BasePlusCommissionEmployee


Fig. G.9 | BasePlusCommissionEmployee inherits protected instance variables from CommissionEmployee.

When you create a BasePlusCommissionEmployee object, it contains all instance variables declared in the class hierarchy to that point—i.e., those from classes Object, CommissionEmployee and BasePlusCommissionEmployee. Class BasePlusCommissionEmployee does not inherit class CommissionEmployee’s constructor. However, class BasePlusCommissionEmployee’s six-argument constructor (lines 10–15) calls class Commission-Employee’s five-argument constructor explicitly to initialize the instance variables that BasePlusCommissionEmployee inherited from class CommissionEmployee. Similarly, class CommissionEmployee’s constructor implicitly calls class Object’s constructor. BasePlusCommissionEmployee’s constructor must do this explicitly because CommissionEmployee does not provide a no-argument constructor that could be invoked implicitly.

Testing Class BasePlusCommissionEmployee

The BasePlusCommissionEmployeeTest class for this example is identical to that of Fig. G.7 and produces the same output, so we do not show it here. Although the version of class BasePlusCommissionEmployee in Fig. G.6 does not use inheritance and the version in Fig. G.9 does, both classes provide the same functionality. The source code in Fig. G.9 (51 lines) is considerably shorter than that in Fig. G.6 (128 lines), because most of BasePlusCommissionEmployee’s functionality is now inherited from CommissionEmployee—there’s now only one copy of the CommissionEmployee functionality. This makes the code easier to maintain, modify and debug, because the code related to a commission employee exists only in class CommissionEmployee.

Notes on Using protected Instance Variables

In this example, we declared superclass instance variables as protected so that subclasses could access them. Inheriting protected instance variables slightly increases performance, because we can directly access the variables in the subclass without incurring the overhead of a set or get method call. In most cases, however, it’s better to use private instance variables to encourage proper software engineering, and leave code optimization issues to the compiler. Your code will be easier to maintain, modify and debug.

Using protected instance variables creates several potential problems. First, the subclass object can set an inherited variable’s value directly without using a set method. Therefore, a subclass object can assign an invalid value to the variable, possibly leaving the object in an inconsistent state. For example, if we were to declare CommissionEmployee’s instance variable grossSales as protected, a subclass object (e.g., BasePlusCommissionEmployee) could then assign a negative value to grossSales. Another problem with using protected instance variables is that subclass methods are more likely to be written so that they depend on the superclass’s data implementation. In practice, subclasses should depend only on the superclass services (i.e., non-private methods) and not on the superclass data implementation. With protected instance variables in the superclass, we may need to modify all the subclasses of the superclass if the superclass implementation changes. For example, if for some reason we were to change the names of instance variables firstName and lastName to first and last, then we would have to do so for all occurrences in which a subclass directly references superclass instance variables firstName and lastName. In such a case, the software is said to be fragile or brittle, because a small change in the superclass can “break” subclass implementation. You should be able to change the superclass implementation while still providing the same services to the subclasses. Of course, if the superclass services change, we must reimplement our subclasses. A third problem is that a class’s protected members are visible to all classes in the same package as the class containing the protected members—this is not always desirable.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset