G.10 Case Study: Payroll System Using Polymorphism

This section reexamines the CommissionEmployeeBasePlusCommissionEmployee hierarchy that we explored throughout Section G.4. Now we use an abstract method and polymorphism to perform payroll calculations based on an enhanced employee inheritance hierarchy that meets the following requirements:

A company pays its employees on a weekly basis. The employees are of four types: Salaried employees are paid a fixed weekly salary regardless of the number of hours worked, hourly employees are paid by the hour and receive overtime pay (i.e., 1.5 times their hourly salary rate) for all hours worked in excess of 40 hours, commission employees are paid a percentage of their sales and base-salaried commission employees receive a base salary plus a percentage of their sales. For the current pay period, the company has decided to reward salaried-commission employees by adding 10% to their base salaries. The company wants to write an application that performs its payroll calculations polymorphically.

We use abstract class Employee to represent the general concept of an employee. The classes that extend Employee are SalariedEmployee, CommissionEmployee and HourlyEmployee. Class BasePlusCommissionEmployee—which extends CommissionEmployee—represents the last employee type. The UML class diagram in Fig. G.14 shows the inheritance hierarchy for our polymorphic employee-payroll application. Abstract class name Employee is italicized—a convention of the UML.

Image

Fig. G.14 | Employee hierarchy UML class diagram.

Abstract superclass Employee declares the “interface” to the hierarchy—that is, the set of methods that a program can invoke on all Employee objects. We use the term “interface” here in a general sense to refer to the various ways programs can communicate with objects of any Employee subclass. Be careful not to confuse the general notion of an “interface” with the formal notion of a Java interface, the subject of Section G.12. Each employee, regardless of the way his or her earnings are calculated, has a first name, a last name and a social security number, so private instance variables firstName, lastName and socialSecurityNumber appear in abstract superclass Employee.

The following sections implement the Employee class hierarchy of Fig. G.14. The first section implements abstract superclass Employee. The next four sections each implement one of the concrete classes. The last section implements a test program that builds objects of all these classes and processes those objects polymorphically.

G.10.1 Abstract Superclass Employee

Class Employee (Fig. G.16) provides methods earnings and toString, in addition to the get and set methods that manipulate Employee’s instance variables. An earnings method certainly applies generically to all employees. But each earnings calculation depends on the employee’s class. So we declare earnings as abstract in superclass Employee because a default implementation does not make sense for that method—there isn’t enough information to determine what amount earnings should return. Each subclass overrides earnings with an appropriate implementation. To calculate an employee’s earnings, the program assigns to a superclass Employee variable a reference to the employee’s object, then invokes the earnings method on that variable. We maintain an array of Employee variables, each holding a reference to an Employee object. (Of course, there cannot be Employee objects, because Employee is an abstract class. Because of inheritance, however, all objects of all subclasses of Employee may nevertheless be thought of as Employee objects.) The program will iterate through the array and call method earnings for each Employee object. Java processes these method calls polymorphically. Declaring earnings as an abstract method in Employee enables the calls to earnings through Employee variables to compile and forces every direct concrete subclass of Employee to override earnings.

Method toString in class Employee returns a String containing the first name, last name and social security number of the employee. As we’ll see, each subclass of Employee overrides method toString to create a String representation of an object of that class that contains the employee’s type (e.g., "salaried employee:") followed by the rest of the employee’s information.

The diagram in Fig. G.15 shows each of the five classes in the hierarchy down the left side and methods earnings and toString across the top. For each class, the diagram shows the desired results of each method. We do not list superclass Employee’s get and set methods because they’re not overridden in any of the subclasses—each of these methods is inherited and used “as is” by each subclass.

Image

Fig. G.15 | Polymorphic interface for the Employee hierarchy classes.

Let’s consider class Employee’s declaration (Fig. G.16). The class includes a constructor that takes the first name, last name and social security number as arguments (lines 11–16); get methods that return the first name, last name and social security number (lines 25–28, 37–40 and 49–52, respectively); set methods that set the first name, last name and social security number (lines 19–22, 31–34 and 43–46, respectively); method toString (lines 55–60), which returns the String representation of an Employee; and abstract method earnings (line 63), which will be implemented by each of the concrete subclasses. The Employee constructor does not validate its parameters in this example; normally, such validation should be provided.


 1   // Fig. G.16: Employee.java
 2   // Employee abstract superclass.
 3
 4   public abstract class Employee
 5   {
 6      private String firstName;
 7      private String lastName;
 8      private String socialSecurityNumber;
 9
10      // three-argument constructor
11      public Employee( String first, String last, String ssn )
12      {
13         firstName = first;
14         lastName = last;
15         socialSecurityNumber = ssn;s
16      } // end three-argument Employee constructor
17
18      // set first name
19      public void setFirstName( String first )
20      {
21         firstName = first; // should validate
22      } // end method setFirstName
23
24      // return first name
25      public String getFirstName()
26      {
27         return firstName;
28      } // end method getFirstName
29
30      // set last name
31      public void setLastName( String last )
32      {
33         lastName = last; // should validate
34      } // end method setLastName
35
36      // return last name
37      public String getLastName()
38      {
39         return lastName;
40      } // end method getLastName
41
42      // set social security number
43      public void setSocialSecurityNumber( String ssn )
44      {
45         socialSecurityNumber = ssn; // should validate
46      } // end method setSocialSecurityNumber
47
48      // return social security number
49      public String getSocialSecurityNumber()
50      {
51         return socialSecurityNumber;
52      } // end method getSocialSecurityNumber
53
54      // return String representation of Employee object
55      @Override
56      public String toString()
57      {
58         return String.format( "%s %s social security number: %s",
59            getFirstName(), getLastName(), getSocialSecurityNumber() );
60      } // end method toString
61
62      // abstract method overridden by concrete subclasses        
63      public abstract double earnings(); // no implementation here
64   } // end abstract class Employee


Fig. G.16 | Employee abstract superclass.

Why did we decide to declare earnings as an abstract method? It simply does not make sense to provide an implementation of this method in class Employee. We cannot calculate the earnings for a general Employee—we first must know the specific type of Employee to determine the appropriate earnings calculation. By declaring this method abstract, we indicate that each concrete subclass must provide an appropriate earnings implementation and that a program will be able to use superclass Employee variables to invoke method earnings polymorphically for any type of Employee.

G.10.2 Concrete Subclass SalariedEmployee

Class SalariedEmployee (Fig. G.17) extends class Employee (line 4) and overrides abstract method earnings (lines 33–37), which makes SalariedEmployee a concrete class. The class includes a constructor (lines 9–14) that takes a first name, a last name, a social security number and a weekly salary as arguments; a set method to assign a new nonnegative value to instance variable weeklySalary (lines 17–24); a get method to return weeklySalary’s value (lines 27–30); a method earnings (lines 33–37) to calculate a SalariedEmployee’s earnings; and a method toString (lines 40–45), which returns a String including the employee’s type, namely, "salaried employee: " followed by employee-specific information produced by superclass Employee’s toString method and SalariedEmployee’s getWeeklySalary method. Class SalariedEmployee’s constructor passes the first name, last name and social security number to the Employee constructor (line 12) to initialize the private instance variables not inherited from the superclass. Method earnings overrides Employee’s abstract method earnings to provide a concrete implementation that returns the SalariedEmployee’s weekly salary. If we do not implement earnings, class SalariedEmployee must be declared abstract—otherwise, class SalariedEmployee will not compile. Of course, we want SalariedEmployee to be a concrete class in this example.

Method toString (lines 40–45) overrides Employee method toString. If class SalariedEmployee did not override toString, SalariedEmployee would have inherited the Employee version of toString. In that case, SalariedEmployee’s toString method would simply return the employee’s full name and social security number, which does not adequately represent a SalariedEmployee. To produce a complete String representation of a SalariedEmployee, the subclass’s toString method returns "salaried employee: " followed by the superclass Employee-specific information (i.e., first name, last name and social security number) obtained by invoking the superclass’s toString method (line 44)—this is a nice example of code reuse. The String representation of a SalariedEmployee also contains the employee’s weekly salary obtained by invoking the class’s getWeeklySalary method.


 1   // Fig. G.17: SalariedEmployee.java
 2   // SalariedEmployee concrete class extends abstract class Employee.
 3
 4   public class SalariedEmployee extends Employee
 5   {
 6      private double weeklySalary;
 7
 8      // four-argument constructor
 9      public SalariedEmployee( String first, String last, String ssn,
10         double salary )
11      {
12         super( first, last, ssn ); // pass to Employee constructor
13         setWeeklySalary( salary ); // validate and store salary
14      } // end four-argument SalariedEmployee constructor
15
16      // set salary
17      public void setWeeklySalary( double salary )
18      {
19         if ( salary >= 0.0 )
20            baseSalary = salary;
21         else
22            throw new IllegalArgumentException(
23               "Weekly salary must be >= 0.0" );
24      } // end method setWeeklySalary
25
26      // return salary
27      public double getWeeklySalary()
28      {
29         return weeklySalary;
30      } // end method getWeeklySalary
31
32      // calculate earnings; override abstract method earnings in Employee
33      @Override                                                           
34      public double earnings()                                            
35      {                                                                   
36         return getWeeklySalary();                                        
37      } // end method earnings                                            
38
39      // return String representation of SalariedEmployee object   
40      @Override                                                    
41      public String toString()                                     
42      {                                                            
43         return String.format( "salaried employee: %s %s: $%,.2f",
44            super.toString(), "weekly salary", getWeeklySalary() );
45      } // end method toString                                     
46   } // end class SalariedEmployee


Fig. G.17 | SalariedEmployee concrete class extends abstract class Employee.

G.10.3 Concrete Subclass HourlyEmployee

Class HourlyEmployee (Fig. G.18) also extends Employee (line 4). The class includes a constructor (lines 10–16) that takes as arguments a first name, a last name, a social security number, an hourly wage and the number of hours worked. Lines 19–26 and 35–42 declare set methods that assign new values to instance variables wage and hours, respectively. Method setWage (lines 19–26) ensures that wage is nonnegative, and method setHours (lines 35–42) ensures that hours is between 0 and 168 (the total number of hours in a week) inclusive. Class HourlyEmployee also includes get methods (lines 29–32 and 45–48) to return the values of wage and hours, respectively; a method earnings (lines 51–58) to calculate an HourlyEmployee’s earnings; and a method toString (lines 61–67), which returns a String containing the employee’s type ("hourly employee: ") and the employee-specific information. The HourlyEmployee constructor, like the SalariedEmployee constructor, passes the first name, last name and social security number to the superclass Employee constructor (line 13) to initialize the private instance variables. In addition, method toString calls superclass method toString (line 65) to obtain the Employee-specific information (i.e., first name, last name and social security number)—this is another nice example of code reuse.


 1   // Fig. G.18: HourlyEmployee.java
 2   // HourlyEmployee class extends Employee.
 3
 4   public class HourlyEmployee extends Employee
 5   {
 6      private double wage; // wage per hour
 7      private double hours; // hours worked for week
 8
 9      // five-argument constructor
10      public HourlyEmployee( String first, String last, String ssn,
11         double hourlyWage, double hoursWorked )
12      {
13         super( first, last, ssn );
14         setWage( hourlyWage ); // validate hourly wage
15         setHours( hoursWorked ); // validate hours worked
16      } // end five-argument HourlyEmployee constructor
17
18      // set wage
19      public void setWage( double hourlyWage )
20      {
21         if ( hourlyWage >= 0.0 )
22            wage = hourlyWage;
23         else
24            throw new IllegalArgumentException(
25               "Hourly wage must be >= 0.0" );
26      } // end method setWage
27
28      // return wage
29      public double getWage()
30      {
31         return wage;
32      } // end method getWage
33
34      // set hours worked
35      public void setHours( double hoursWorked )
36      {
37         if ( ( hoursWorked >= 0.0 ) && ( hoursWorked <= 168.0 ) )
38            hours = hoursWorked;
39         else
40            throw new IllegalArgumentException(
41               "Hours worked must be >= 0.0 and <= 168.0" );
42      } // end method setHours
43
44      // return hours worked
45      public double getHours()
46      {
47         return hours;
48      } // end method getHours
49
50      // calculate earnings; override abstract method earnings in Employee
51      @Override                                                           
52      public double earnings()                                            
53      {                                                                   
54         if ( getHours() <= 40 ) // no overtime                           
55            return getWage() * getHours();                                
56         else                                                             
57            return 40 * getWage() + ( getHours() - 40 ) * getWage() * 1.5;
58      } // end method earnings                                            
59
60      // return String representation of HourlyEmployee object              
61      @Override                                                             
62      public String toString()                                              
63      {                                                                     
64         return String.format( "hourly employee: %s %s: $%,.2f; %s: %,.2f",
65            super.toString(), "hourly wage", getWage(),                     
66            "hours worked", getHours() );                                   
67      } // end method toString                                              
68   } // end class HourlyEmployee


Fig. G.18 | HourlyEmployee class extends Employee.

G.10.4 Concrete Subclass CommissionEmployee

Class CommissionEmployee (Fig. G.19) extends class Employee (line 4). The class includes a constructor (lines 10–16) that takes a first name, a last name, a social security number, a sales amount and a commission rate; set methods (lines 19–26 and 35–42) to assign new values to instance variables commissionRate and grossSales, respectively; get methods (lines 29–32 and 45–48) that retrieve the values of these instance variables; method earnings (lines 51–55) to calculate a CommissionEmployee’s earnings; and method toString (lines 58–65), which returns the employee’s type, namely, "commission employee: " and employee-specific information. The constructor also passes the first name, last name and social security number to Employee’s constructor (line 13) to initialize Employee’s private instance variables. Method toString calls superclass method toString (line 62) to obtain the Employee-specific information (i.e., first name, last name and social security number).


 1   // Fig. G.19: CommissionEmployee.java
 2   // CommissionEmployee class extends Employee.
 3
 4   public class CommissionEmployee extends Employee
 5   {
 6      private double grossSales; // gross weekly sales
 7      private double commissionRate; // commission percentage
 8
 9      // five-argument constructor
10      public CommissionEmployee( String first, String last, String ssn,
11         double sales, double rate )
12      {
13         super( first, last, ssn );
14         setGrossSales( sales );
15         setCommissionRate( rate );
16      } // end five-argument CommissionEmployee constructor
17
18      // set commission rate
19      public void setCommissionRate( double rate )
20      {
21         if ( rate > 0.0 && rate < 1.0 )
22            commissionRate = rate;
23         else
24            throw new IllegalArgumentException(
25               "Commission rate must be > 0.0 and < 1.0" );
26      } // end method setCommissionRate
27
28      // return commission rate
29      public double getCommissionRate()
30      {
31         return commissionRate;
32      } // end method getCommissionRate
33
34      // set gross sales amount
35      public void setGrossSales( double sales )
36      {
37         if ( sales >= 0.0 )
38            grossSales = sales;
39         else
40            throw new IllegalArgumentException(
41               "Gross sales must be >= 0.0" );
42      } // end method setGrossSales
43
44      // return gross sales amount
45      public double getGrossSales()
46      {
47         return grossSales;
48      } // end method getGrossSales
49
50      // calculate earnings; override abstract method earnings in Employee
51      @Override                                                           
52      public double earnings()                                            
53      {                                                                   
54         return getCommissionRate() * getGrossSales();                    
55      } // end method earnings                                            
56
57      // return String representation of CommissionEmployee object
58      @Override                                                   
59      public String toString()                                    
60      {                                                           
61         return String.format( "%s: %s %s: $%,.2f; %s: %.2f",    
62            "commission employee", super.toString(),              
63            "gross sales", getGrossSales(),                       
64            "commission rate", getCommissionRate() );             
65      } // end method toString                                    
66   } // end class CommissionEmployee


Fig. G.19 | CommissionEmployee class extends Employee.

G.10.5 Indirect Concrete Subclass BasePlusCommissionEmployee

Class BasePlusCommissionEmployee (Fig. G.20) extends class CommissionEmployee (line 4) and therefore is an indirect subclass of class Employee. Class BasePlusCommissionEmployee has a constructor (lines 9–14) that takes as arguments a first name, a last name, a social security number, a sales amount, a commission rate and a base salary. It then passes all of these except the base salary to the CommissionEmployee constructor (line 12) to initialize the inherited members. BasePlusCommissionEmployee also contains a set method (lines 17–24) to assign a new value to instance variable baseSalary and a get method (lines 27–30) to return baseSalary’s value. Method earnings (lines 33–37) calculates a BasePlusCommissionEmployee’s earnings. Line 36 in method earnings calls superclass CommissionEmployee’s earnings method to calculate the commission-based portion of the employee’s earnings—this is another nice example of code reuse. BasePlusCommissionEmployee’s toString method (lines 40–46) creates a String representation of a BasePlusCommissionEmployee that contains "base-salaried", followed by the String


 1   // Fig. G.20: BasePlusCommissionEmployee.java
 2   // BasePlusCommissionEmployee class extends CommissionEmployee.
 3
 4   public class BasePlusCommissionEmployee extends CommissionEmployee
 5   {
 6      private double baseSalary; // base salary per week
 7
 8      // six-argument constructor
 9      public BasePlusCommissionEmployee( 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      } // end six-argument BasePlusCommissionEmployee constructor
15
16      // set base salary
17      public void setBaseSalary( double salary )
18      {
19         if ( salary >= 0.0 )
20            baseSalary = salary;
21         else
22            throw new IllegalArgumentException(
23               "Base salary must be >= 0.0" );
24      } // end method setBaseSalary
25
26      // return base salary
27      public double getBaseSalary()
28      {
29         return baseSalary;
30      } // end method getBaseSalary
31
32      // calculate earnings; override method earnings in CommissionEmployee
33      @Override                                                            
34      public double earnings()                                             
35      {                                                                    
36         return getBaseSalary() + super.earnings();                        
37      } // end method earnings                                             
38
39      // return String representation of BasePlusCommissionEmployee object
40      @Override                                                           
41      public String toString()                                            
42      {                                                                   
43         return String.format( "%s %s; %s: $%,.2f",                       
44            "base-salaried", super.toString(),                            
45            "base salary", getBaseSalary() );                             
46      } // end method toString                                            
47   } // end class BasePlusCommissionEmployee


Fig. G.20 | BasePlusCommissionEmployee class extends CommissionEmployee.

obtained by invoking superclass CommissionEmployee’s toString method (another example of code reuse), then the base salary. The result is a String beginning with "base-salaried commission employee" followed by the rest of the BasePlusCommissionEmployee’s information. Recall that CommissionEmployee’s toString obtains the employee’s first name, last name and social security number by invoking the toString method of its superclass (i.e., Employee)—yet another example of code reuse. BasePlusCommissionEmployee’s toString initiates a chain of method calls that span all three levels of the Employee hierarchy.

G.10.6 Polymorphic Processing, Operator instanceof and Downcasting

To test our Employee hierarchy, the application in Fig. G.21 creates an object of each of the four concrete classes SalariedEmployee, HourlyEmployee, CommissionEmployee and BasePlusCommissionEmployee. The program manipulates these objects nonpolymorphically, via variables of each object’s own type, then polymorphically, using an array of Employee variables. While processing the objects polymorphically, the program increases the base salary of each BasePlusCommissionEmployee by 10%—this requires determining the object’s type at execution time. Finally, the program polymorphically determines and outputs the type of each object in the Employee array. Lines 9–18 create objects of each of the four concrete Employee subclasses. Lines 22–30 output the String representation and earnings of each of these objects nonpolymorphically. Each object’s toString method is called implicitly by printf when the object is output as a String with the %s format specifier.


 1   // Fig. G.21: PayrollSystemTest.java
 2   // Employee hierarchy test program.
 3
 4   public class PayrollSystemTest
 5   {
 6      public static void main( String[] args )
 7      {
 8         // create subclass objects                                          
 9         SalariedEmployee salariedEmployee =                                 
10            new SalariedEmployee( "John", "Smith", "111-11-1111", 800.00 );  
11         HourlyEmployee hourlyEmployee =                                     
12            new HourlyEmployee( "Karen", "Price", "222-22-2222", 16.75, 40 );
13         CommissionEmployee commissionEmployee =                             
14            new CommissionEmployee(                                          
15            "Sue", "Jones", "333-33-3333", 10000, .06 );                     
16         BasePlusCommissionEmployee basePlusCommissionEmployee =             
17            new BasePlusCommissionEmployee(                                  
18            "Bob", "Lewis", "444-44-4444", 5000, .04, 300 );                 
19
20         System.out.println( "Employees processed individually: " );
21
22         System.out.printf( "%s %s: $%,.2f ",
23            salariedEmployee, "earned", salariedEmployee.earnings() );
24         System.out.printf( "%s %s: $%,.2f ",
25            hourlyEmployee, "earned", hourlyEmployee.earnings() );
26         System.out.printf( "%s %s: $%,.2f ",
27            commissionEmployee, "earned", commissionEmployee.earnings() );
28         System.out.printf( "%s %s: $%,.2f ",
29            basePlusCommissionEmployee,
30            "earned", basePlusCommissionEmployee.earnings() );
31
32         // create four-element Employee array
33         Employee[] employees = new Employee[ 4 ];
34
35         // initialize array with Employees          
36         employees[ 0 ] = salariedEmployee;          
37         employees[ 1 ] = hourlyEmployee;            
38         employees[ 2 ] = commissionEmployee;        
39         employees[ 3 ] = basePlusCommissionEmployee;
40
41         System.out.println( "Employees processed polymorphically: " );
42
43         // generically process each element in array employees
44         for ( Employee currentEmployee : employees )
45         {
46            System.out.println( currentEmployee ); // invokes toString
47
48            // determine whether element is a BasePlusCommissionEmployee
49            if ( currentEmployee instanceof BasePlusCommissionEmployee )
50            {
51               // downcast Employee reference to
52               // BasePlusCommissionEmployee reference
53               BasePlusCommissionEmployee employee =
54                  ( BasePlusCommissionEmployee ) currentEmployee;
55
56               employee.setBaseSalary( 1.10 * employee.getBaseSalary() );
57
58               System.out.printf(
59                  "new base salary with 10%% increase is: $%,.2f ",
60                  employee.getBaseSalary() );
61            } // end if
62
63            System.out.printf(
64               "earned $%,.2f ", currentEmployee.earnings() );
65         } // end for
66
67         // get type name of each object in employees array
68         for ( int j = 0; j < employees.length; j++ )      
69            System.out.printf( "Employee %d is a %s ", j, 
70               employees[ j ].getClass().getName() );      
71      } // end main
72   } // end class PayrollSystemTest


Employees processed individually:

salaried employee: John Smith
social security number: 111-11-1111
weekly salary: $800.00
earned: $800.00

hourly employee: Karen Price
social security number: 222-22-2222
hourly wage: $16.75; hours worked: 40.00
earned: $670.00

commission employee: Sue Jones
social security number: 333-33-3333
gross sales: $10,000.00; commission rate: 0.06
earned: $600.00

base-salaried commission employee: Bob Lewis
social security number: 444-44-4444
gross sales: $5,000.00; commission rate: 0.04; base salary: $300.00
earned: $500.00


Employees processed polymorphically:

salaried employee: John Smith
social security number: 111-11-1111
weekly salary: $800.00
earned $800.00

hourly employee: Karen Price
social security number: 222-22-2222
hourly wage: $16.75; hours worked: 40.00
earned $670.00

commission employee: Sue Jones
social security number: 333-33-3333
gross sales: $10,000.00; commission rate: 0.06
earned $600.00

base-salaried commission employee: Bob Lewis
social security number: 444-44-4444
gross sales: $5,000.00; commission rate: 0.04; base salary: $300.00
new base salary with 10% increase is: $330.00
earned $530.00                               

Employee 0 is a SalariedEmployee
Employee 1 is a HourlyEmployee
Employee 2 is a CommissionEmployee
Employee 3 is a BasePlusCommissionEmployee


Fig. G.21 | Employee hierarchy test program.

Creating the Array of Employees

Line 33 declares employees and assigns it an array of four Employee variables. Line 36 assigns the reference to a SalariedEmployee object to employees[0]. Line 37 assigns the reference to an HourlyEmployee object to employees[1]. Line 38 assigns the reference to a CommissionEmployee object to employees[2]. Line 39 assigns the reference to a BasePlusCommissionEmployee object to employee[3]. These assignments are allowed, because a SalariedEmployee is an Employee, an HourlyEmployee is an Employee, a CommissionEmployee is an Employee and a BasePlusCommissionEmployee is an Employee. Therefore, we can assign the references of SalariedEmployee, HourlyEmployee, CommissionEmployee and BasePlusCommissionEmployee objects to superclass Employee variables, even though Employee is an abstract class.

Polymorphically Processing Employees

Lines 44–65 iterate through array employees and invoke methods toString and earnings with Employee variable currentEmployee, which is assigned the reference to a different Employee in the array on each iteration. The output illustrates that the appropriate methods for each class are indeed invoked. All calls to method toString and earnings are resolved at execution time, based on the type of the object to which currentEmployee refers. This process is known as dynamic binding or late binding. For example, line 46 implicitly invokes method toString of the object to which currentEmployee refers. As a result of dynamic binding, Java decides which class’s toString method to call at execution time rather than at compile time. Only the methods of class Employee can be called via an Employee variable (and Employee, of course, includes the methods of class Object). A superclass reference can be used to invoke only methods of the superclass—the subclass method implementations are invoked polymorphically.

Performing Type-Specific Operations on BasePlusCommissionEmployees

We perform special processing on BasePlusCommissionEmployee objects—as we encounter these objects at execution time, we increase their base salary by 10%. When processing objects polymorphically, we typically do not need to worry about the “specifics,” but to adjust the base salary, we do have to determine the specific type of Employee object at execution time. Line 49 uses the instanceof operator to determine whether a particular Employee object’s type is BasePlusCommissionEmployee. The condition in line 49 is true if the object referenced by currentEmployee is a BasePlusCommissionEmployee. This would also be true for any object of a BasePlusCommissionEmployee subclass because of the is-a relationship a subclass has with its superclass. Lines 53–54 downcast currentEmployee from type Employee to type BasePlusCommissionEmployee—this cast is allowed only if the object has an is-a relationship with BasePlusCommissionEmployee. The condition at line 49 ensures that this is the case. This cast is required if we’re to invoke subclass BasePlusCommissionEmployee methods getBaseSalary and setBaseSalary on the current Employee object—as you’ll see momentarily, attempting to invoke a subclass-only method directly on a superclass reference is a compilation error.


Image Common Programming Error G.3

Assigning a superclass variable to a subclass variable (without an explicit cast) is a compilation error.


 


Image Software Engineering Observation G.7

If a subclass object’s reference has been assigned to a variable of one of its direct or indirect superclasses at execution time, it’s acceptable to downcast the reference stored in that superclass variable back to a subclass-type reference. Before performing such a cast, use the instanceof operator to ensure that the object is indeed an object of an appropriate subclass.


 


Image Common Programming Error G.4

When downcasting a reference, a ClassCastException occurs if the referenced object at execution time does not have an is-a relationship with the type specified in the cast operator.


If the instanceof expression in line 49 is true, lines 53–60 perform the special processing required for the BasePlusCommissionEmployee object. Using BasePlusCommissionEmployee variable employee, line 56 invokes subclass-only methods getBaseSalary and setBaseSalary to retrieve and update the employee’s base salary with the 10% raise.

Calling earnings Polymorphically

Lines 63–64 invoke method earnings on currentEmployee, which polymorphically calls the appropriate subclass object’s earnings method. Obtaining the earnings of the SalariedEmployee, HourlyEmployee and CommissionEmployee polymorphically in lines 63–64 produces the same results as obtaining these employees’ earnings individually in lines 22–27. The earnings amount obtained for the BasePlusCommissionEmployee in lines 63–64 is higher than that obtained in lines 28–30, due to the 10% increase in its base salary.

Using Reflection to Get Each Employee’s Class Name

Lines 68–70 display each employee’s type as a String, using basic features of Java’s so-called reflection capabilities. Every object knows its own class and can access this information through the getClass method, which all classes inherit from class Object. Method getClass returns an object of type Class (from package java.lang), which contains information about the object’s type, including its class name. Line 70 invokes getClass on the current object to get its runtime class. The result of the getClass call is used to invoke getName to get the object’s class name.

Avoiding Compilation Errors with Downcasting

In the previous example, we avoided several compilation errors by downcasting an Employee variable to a BasePlusCommissionEmployee variable in lines 53–54. If you remove the cast operator (BasePlusCommissionEmployee) from line 54 and attempt to assign Employee variable currentEmployee directly to BasePlusCommissionEmployee variable employee, you’ll receive an “incompatible types” compilation error. This error indicates that the attempt to assign the reference of superclass object currentEmployee to subclass variable employee is not allowed. The compiler prevents this assignment because a CommissionEmployee is not a BasePlusCommissionEmployeethe is-a relationship applies only between the subclass and its superclasses, not vice versa.

Similarly, if lines 56 and 60 used superclass variable currentEmployee to invoke subclass-only methods getBaseSalary and setBaseSalary, we’d receive “cannot find symbol” compilation errors at these lines. Attempting to invoke subclass-only methods via a superclass variable is not allowed—even though lines 56 and 60 execute only if instanceof in line 49 returns true to indicate that currentEmployee holds a reference to a BasePlusCommissionEmployee object. Using a superclass Employee variable, we can invoke only methods found in class Employee—earnings, toString and Employee’s get and set methods.


Image Software Engineering Observation G.8

Although the actual method that’s called depends on the runtime type of the object to which a variable refers, a variable can be used to invoke only those methods that are members of that variable’s type, which the compiler verifies.


G.10.7 Summary of the Allowed Assignments Between Superclass and Subclass Variables

Now that you’ve seen a complete application that processes diverse subclass objects polymorphically, we summarize what you can and cannot do with superclass and subclass objects and variables. Although a subclass object also is a superclass object, the two objects are nevertheless different. As discussed previously, subclass objects can be treated as objects of their superclass. But because the subclass can have additional subclass-only members, assigning a superclass reference to a subclass variable is not allowed without an explicit cast—such an assignment would leave the subclass members undefined for the superclass object.

We’ve discussed four ways to assign superclass and subclass references to variables of superclass and subclass types:

1. Assigning a superclass reference to a superclass variable is straightforward.

2. Assigning a subclass reference to a subclass variable is straightforward.

3. Assigning a subclass reference to a superclass variable is safe, because the subclass object is an object of its superclass. However, the superclass variable can be used to refer only to superclass members. If this code refers to subclass-only members through the superclass variable, the compiler reports errors.

4. Attempting to assign a superclass reference to a subclass variable is a compilation error. To avoid this error, the superclass reference must be cast to a subclass type explicitly. At execution time, if the object to which the reference refers is not a subclass object, an exception will occur. (For more on exception handling, see Appendix H.) You should use the instanceof operator to ensure that such a cast is performed only if the object is a subclass object.

G.11 final Methods and Classes

We saw in Sections D.3 and D.10 that variables can be declared final to indicate that they cannot be modified after they’re initialized—such variables represent constant values. It’s also possible to declare methods, method parameters and classes with the final modifier.

Final Methods Cannot Be Overridden

A final method in a superclass cannot be overridden in a subclass—this guarantees that the final method implementation will be used by all direct and indirect subclasses in the hierarchy. Methods that are declared private are implicitly final, because it’s not possible to override them in a subclass. Methods that are declared static are also implicitly final. A final method’s declaration can never change, so all subclasses use the same method implementation, and calls to final methods are resolved at compile time—this is known as static binding.

Final Classes Cannot Be Superclasses

A final class that’s declared final cannot be a superclass (i.e., a class cannot extend a final class). All methods in a final class are implicitly final. Class String is an example of a final class. If you were allowed to create a subclass of String, objects of that subclass could be used wherever Strings are expected. Since class String cannot be extended, programs that use Strings can rely on the functionality of String objects as specified in the Java API. Making the class final also prevents programmers from creating subclasses that might bypass security restrictions. For more insights on the use of keyword final, visit

and


Image Common Programming Error G.5

Attempting to declare a subclass of a final class is a compilation error.


 


Image Software Engineering Observation G.9

In the Java API, the vast majority of classes are not declared final. This enables inheritance and polymorphism. However, in some cases, it’s important to declare classes final—typically for security reasons.


G.12 Case Study: Creating and Using Interfaces

Our next example (Figs. G.23G.27) reexamines the payroll system of Section G.10. Suppose that the company involved wishes to perform several accounting operations in a single accounts payable application—in addition to calculating the earnings that must be paid to each employee, the company must also calculate the payment due on each of several invoices (i.e., bills for goods purchased). Though applied to unrelated things (i.e., employees and invoices), both operations have to do with obtaining some kind of payment amount. For an employee, the payment refers to the employee’s earnings. For an invoice, the payment refers to the total cost of the goods listed on the invoice. Can we calculate such different things as the payments due for employees and invoices in a single application polymorphically? Does Java offer a capability requiring that unrelated classes implement a set of common methods (e.g., a method that calculates a payment amount)? Java interfaces offer exactly this capability.

Standardizing Interactions

Interfaces define and standardize the ways in which things such as people and systems can interact with one another. For example, the controls on a radio serve as an interface between radio users and a radio’s internal components. The controls allow users to perform only a limited set of operations (e.g., change the station, adjust the volume, choose between AM and FM), and different radios may implement the controls in different ways (e.g., using push buttons, dials, voice commands). The interface specifies what operations a radio must permit users to perform but does not specify how the operations are performed.

Software Objects Communicate Via Interfaces

Software objects also communicate via interfaces. A Java interface describes a set of methods that can be called on an object to tell it, for example, to perform some task or return some piece of information. The next example introduces an interface named Payable to describe the functionality of any object that must be capable of being paid and thus must offer a method to determine the proper payment amount due. An interface declaration begins with the keyword interface and contains only constants and abstract methods. Unlike classes, all interface members must be public, and interfaces may not specify any implementation details, such as concrete method declarations and instance variables. All methods declared in an interface are implicitly public abstract methods, and all fields are implicitly public, static and final. [Note: As of Java SE 5, it became a better programming practice to declare sets of constants as enumerations with keyword enum. See Section D.10 for an introduction to enum and Section F.8 for additional enum details.]


Image Good Programming Practice G.1

According to Chapter 9 of the Java Language Specification, it’s proper style to declare an interface’s methods without keywords public and abstract, because they’re redundant in interface method declarations. Similarly, constants should be declared without keywords public, static and final, because they, too, are redundant.


Using an Interface

To use an interface, a concrete class must specify that it implements the interface and must declare each method in the interface with the signature specified in the interface declaration. To specify that a class implements an interface add the implements keyword and the name of the interface to the end of your class declaration’s first line. A class that does not implement all the methods of the interface is an abstract class and must be declared abstract. Implementing an interface is like signing a contract with the compiler that states, “I will declare all the methods specified by the interface or I will declare my class abstract.”


Image Common Programming Error G.6

Failing to implement any method of an interface in a concrete class that implements the interface results in a compilation error indicating that the class must be declared abstract.


Relating Disparate Types

An interface is often used when disparate (i.e., unrelated) classes need to share common methods and constants. This allows objects of unrelated classes to be processed polymorphically—objects of classes that implement the same interface can respond to the same method calls. You can create an interface that describes the desired functionality, then implement this interface in any classes that require that functionality. For example, in the accounts payable application developed in this section, we implement interface Payable in any class that must be able to calculate a payment amount (e.g., Employee, Invoice).

Interfaces vs. Abstract Classes

An interface is often used in place of an abstract class when there’s no default implementation to inherit—that is, no fields and no default method implementations. Like public abstract classes, interfaces are typically public types. Like a public class, a public interface must be declared in a file with the same name as the interface and the .java file-name extension.

Tagging Interfaces

We’ll see in Appendix J, the notion of “tagging interfaces”—empty interfaces that have no methods or constant values. They’re used to add is-a relationships to classes. For example, in Appendix J we’ll discuss a mechanism called object serialization, which can convert objects to byte representations and can convert those byte representations back to objects. To enable this mechanism to work with your objects, you simply have to mark them as Serializable by adding implements Serializable to the end of your class declaration’s first line. Then, all the objects of your class have the is-a relationship with Serializable.

G.12.1 Developing a Payable Hierarchy

To build an application that can determine payments for employees and invoices alike, we first create interface Payable, which contains method getPaymentAmount that returns a double amount that must be paid for an object of any class that implements the interface. Method getPaymentAmount is a general-purpose version of method earnings of the Employee hierarchy—method earnings calculates a payment amount specifically for an Employee, while getPaymentAmount can be applied to a broad range of unrelated objects. After declaring interface Payable, we introduce class Invoice, which implements interface Payable. We then modify class Employee such that it also implements interface Payable. Finally, we update Employee subclass SalariedEmployee to “fit” into the Payable hierarchy by renaming SalariedEmployee method earnings as getPaymentAmount.


Image Good Programming Practice G.2

When declaring a method in an interface, choose a method name that describes the method’s purpose in a general manner, because the method may be implemented by many unrelated classes.


Classes Invoice and Employee both represent things for which the company must be able to calculate a payment amount. Both classes implement the Payable interface, so a program can invoke method getPaymentAmount on Invoice objects and Employee objects alike. As we’ll soon see, this enables the polymorphic processing of Invoices and Employees required for the company’s accounts payable application.

The UML class diagram in Fig. G.22 shows the hierarchy used in our accounts payable application. The hierarchy begins with interface Payable. The UML distinguishes an interface from other classes by placing the word “interface” in guillemets (« and ») above the interface name. The UML expresses the relationship between a class and an interface through a relationship known as realization. A class is said to “realize,” or implement, the methods of an interface. A class diagram models a realization as a dashed arrow with a hollow arrowhead pointing from the implementing class to the interface. The diagram in Fig. G.22 indicates that classes Invoice and Employee each realize (i.e., implement) interface Payable. As in the class diagram of Fig. G.14, class Employee appears in italics, indicating that it’s an abstract class. Concrete class SalariedEmployee extends Employee and inherits its superclass’s realization relationship with interface Payable.

Image

Fig. G.22 | Payable interface hierarchy UML class diagram.

G.12.2 Interface Payable

The declaration of interface Payable begins in Fig. G.23 at line 4. Interface Payable contains public abstract method getPaymentAmount (line 6). The method is not explicitly declared public or abstract. Interface methods are always public and abstract, so they do not need to be declared as such. Interface Payable has only one method—interfaces can have any number of methods. In addition, method getPaymentAmount has no parameters, but interface methods can have parameters. Interfaces may also contain fields that are implicitly final and static.


 1   // Fig. G.23: Payable.java
 2   // Payable interface declaration.
 3
 4    public interface Payable                                             
 5    {                                                                    
 6       double getPaymentAmount(); // calculate payment; no implementation
 7    } // end interface Payable                                           


Fig. G.23 | Payable interface declaration.

G.12.3 Class Invoice

We now create class Invoice (Fig. G.24) to represent a simple invoice that contains billing information for only one kind of part. The class declares private instance variables partNumber, partDescription, quantity and pricePerItem (in lines 6–9) that indicate the part number, a description of the part, the quantity of the part ordered and the price per item. Class Invoice also contains a constructor (lines 12–19), get and set methods (lines 22–74) that manipulate the class’s instance variables and a toString method (lines 77–83) that returns a String representation of an Invoice object. Methods setQuantity (lines 46–52) and setPricePerItem (lines 61–68) ensure that quantity and pricePerItem obtain only nonnegative values.


 1   // Fig. G.24: Invoice.java
 2   // Invoice class that implements Payable.
 3
 4   public class Invoice implements Payable
 5   {
 6      private String partNumber;
 7      private String partDescription;
 8      private int quantity;2
 9      private double pricePerItem;
10
11      // four-argument constructor
12      public Invoice( String part, String description, int count,
13         double price )
14      {
15         partNumber = part;
16         partDescription = description;
17         setQuantity( count ); // validate and store quantity
18         setPricePerItem( price ); // validate and store price per item
19      } // end four-argument Invoice constructor
20
21      // set part number
22      public void setPartNumber( String part )
23      {
24         partNumber = part; // should validate
25      } // end method setPartNumber
26
27      // get part number
28      public String getPartNumber()
29      {
30         return partNumber;
31      } // end method getPartNumber
32
33      // set description
34      public void setPartDescription( String description )
35      {
36         partDescription = description; // should validate
37      } // end method setPartDescription
38
39      // get description
40      public String getPartDescription()
41      {
42         return partDescription;
43      } // end method getPartDescription
44
45      // set quantity
46      public void setQuantity( int count )
47      {
48         if ( count >= 0 )
49            quantity = count;
50         else
51            throw new IllegalArgumentException( "Quantity must be >= 0" );
52      } // end method setQuantity
53
54      // get quantity
55      public int getQuantity()
56      {
57         return quantity;
58      } // end method getQuantity
59
60      // set price per item
61      public void setPricePerItem( double price )
62      {
63         if ( price >= 0.0 )
64            pricePerItem = price;
65         else
66            throw new IllegalArgumentException(
67               "Price per item must be >= 0" );
68      } // end method setPricePerItem
69
70      // get price per item
71      public double getPricePerItem()
72      {
73         return pricePerItem;
74      } // end method getPricePerItem
75
76      // return String representation of Invoice object
77      @Override
78      public String toString()
79      {
80         return String.format( "%s: %s: %s (%s) %s: %d %s: $%,.2f",
81            "invoice", "part number", getPartNumber(), getPartDescription(),
82            "quantity", getQuantity(), "price per item", getPricePerItem() );
83      } // end method toString
84
85      // method required to carry out contract with interface Payable     
86      @Override                                                           
87      public double getPaymentAmount()                                    
88      {                                                                   
89         return getQuantity() * getPricePerItem(); // calculate total cost
90      } // end method getPaymentAmount                                    
91   } // end class Invoice


Fig. G.24 | Invoice class that implements Payable.

Line 4 indicates that class Invoice implements interface Payable. Like all classes, class Invoice also implicitly extends Object. Java does not allow subclasses to inherit from more than one superclass, but it allows a class to inherit from one superclass and implement as many interfaces as it needs. To implement more than one interface, use a comma-separated list of interface names after keyword implements in the class declaration, as in:

public class ClassName extends SuperclassName implements FirstInterface,
    SecondInterface, ...


Image Software Engineering Observation G.10

All objects of a class that implement multiple interfaces have the is-a relationship with each implemented interface type.


Class Invoice implements the one method in interface Payable—method getPaymentAmount is declared in lines 86–90. The method calculates the total payment required to pay the invoice. The method multiplies the values of quantity and pricePerItem (obtained through the appropriate get methods) and returns the result (line 89). This method satisfies the implementation requirement for this method in interface Payable—we’ve fulfilled the interface contract with the compiler.

G.12.4 Modifying Class Employee to Implement Interface Payable

We now modify class Employee such that it implements interface Payable. Figure G.25 contains the modified class, which is identical to that of Fig. G.16 with two exceptions. First, line 4 of Fig. G.25 indicates that class Employee now implements interface Payable. So we must rename earnings to getPaymentAmount throughout the Employee hierarchy. As with method earnings in the version of class Employee in Fig. G.16, however, it does not make sense to implement method getPaymentAmount in class Employee because we cannot calculate the earnings payment owed to a general Employee—we must first know the specific type of Employee. In Fig. G.16, we declared method earnings as abstract for this reason, so class Employee had to be declared abstract. This forced each Employee concrete subclass to override earnings with an implementation.

In Fig. G.25, we handle this situation differently. Recall that when a class implements an interface, it makes a contract with the compiler stating either that the class will implement each of the methods in the interface or that the class will be declared abstract. If the latter option is chosen, we do not need to declare the interface methods as abstract in the abstract class—they’re already implicitly declared as such in the interface. Any concrete subclass of the abstract class must implement the interface methods to fulfill the superclass’s contract with the compiler. If the subclass does not do so, it too must be declared abstract. As indicated by the comments in lines 62–63, class Employee of Fig. G.25 does not implement method getPaymentAmount, so the class is declared abstract. Each direct Employee subclass inherits the superclass’s contract to implement method getPaymentAmount and thus must implement this method to become a concrete class for which objects can be instantiated. A class that extends one of Employee’s concrete subclasses will inherit an implementation of getPaymentAmount and thus will also be a concrete class.


 1   // Fig. G.25: Employee.java
 2   // Employee abstract superclass that implements Payable.
 3
 4   public abstract class Employee implements Payable
 5   {
 6      private String firstName;
 7      private String lastName;
 8      private String socialSecurityNumber;
 9
10      // three-argument constructor
11      public Employee( String first, String last, String ssn )
12      {
13         firstName = first;
14         lastName = last;
15         socialSecurityNumber = ssn;
16      } // end three-argument Employee constructor
17
18      // set first name
19      public void setFirstName( String first )
20      {
21         firstName = first; // should validate
22      } // end method setFirstName
23
24      // return first name
25      public String getFirstName()
26      {
27         return firstName;
28      } // end method getFirstName
29
30      // set last name
31      public void setLastName( String last )
32      {
33         lastName = last; // should validate
34      } // end method setLastName
35
36      // return last name
37      public String getLastName()
38      {
39         return lastName;
40      } // end method getLastName
41
42      // set social security number
43      public void setSocialSecurityNumber( String ssn )
44      {
45         socialSecurityNumber = ssn; // should validate
46      } // end method setSocialSecurityNumber
47
48      // return social security number
49      public String getSocialSecurityNumber()
50      {
51         return socialSecurityNumber;
52      } // end method getSocialSecurityNumber
53
54      // return String representation of Employee object
55      @Override
56      public String toString()
57      {
58         return String.format( "%s %s social security number: %s",
59            getFirstName(), getLastName(), getSocialSecurityNumber() );
60      } // end method toString
61
62      // Note: We do not implement Payable method getPaymentAmount here so 
63      // this class must be declared abstract to avoid a compilation error.
64   } // end abstract class Employee


Fig. G.25 | Employee abstract superclass that implements Payable.

G.12.5 Modifying Class SalariedEmployee for Use in the Payable Hierarchy

Figure G.26 contains a modified SalariedEmployee class that extends Employee and fulfills superclass Employee’s contract to implement Payable method getPaymentAmount. This version of SalariedEmployee is identical to that of Fig. G.17, but it replaces method earnings with method getPaymentAmount (lines 34–38). Recall that the Payable version of the method has a more general name to be applicable to possibly disparate classes. The remaining Employee subclasses (e.g., HourlyEmployee, CommissionEmployee and BasePlusCommissionEmployee) also must be modified to contain method getPaymentAmount in place of earnings to reflect the fact that Employee now implements Payable. We leave these modifications as an exercise (Exercise G.16) and use only SalariedEmployee in our test program here. Exercise G.17 asks you to implement interface Payable in the entire Employee class hierarchy of Figs. G.16G.21 without modifying the Employee subclasses.

When a class implements an interface, the same is-a relationship provided by inheritance applies. Class Employee implements Payable, so we can say that an Employee is a Payable. In fact, objects of any classes that extend Employee are also Payable objects. SalariedEmployee objects, for instance, are Payable objects. Objects of any subclasses of the class that implements the interface can also be thought of as objects of the interface type. Thus, just as we can assign the reference of a SalariedEmployee object to a superclass Employee variable, we can assign the reference of a SalariedEmployee object to an interface Payable variable. Invoice implements Payable, so an Invoice object also is a Payable object, and we can assign the reference of an Invoice object to a Payable variable.


Image Software Engineering Observation G.11

When a method parameter is declared with a superclass or interface type, the method processes the object received as an argument polymorphically.


 


Image Software Engineering Observation G.12

Using a superclass reference, we can polymorphically invoke any method declared in the superclass and its superclasses (e.g., class Object). Using an interface reference, we can polymorphically invoke any method declared in the interface, its superinterfaces (one interface can extend another) and in class Object—a variable of an interface type must refer to an object to call methods, and all objects have the methods of class Object.



 1   // Fig. G.26: SalariedEmployee.java
 2   // SalariedEmployee class extends Employee, which implements Payable.
 3
 4   public class SalariedEmployee extends Employee
 5   {
 6      private double weeklySalary;
 7
 8      // four-argument constructor
 9      public SalariedEmployee( String first, String last, String ssn,
10         double salary )
11      {
12         super( first, last, ssn ); // pass to Employee constructor
13         setWeeklySalary( salary ); // validate and store salary
14      } // end four-argument SalariedEmployee constructor
15
16      // set salary
17      public void setWeeklySalary( double salary )
18      {
19         if ( salary >= 0.0 )
20            baseSalary = salary;
21         else
22            throw new IllegalArgumentException(
23               "Weekly salary must be >= 0.0" );
24      } // end method setWeeklySalary
25
26      // return salary
27      public double getWeeklySalary()
28      {
29         return weeklySalary;
30      } // end method getWeeklySalary
31
32      // calculate earnings; implement interface Payable method that was
33      // abstract in superclass Employee                                
34      @Override
35      public double getPaymentAmount()                                  
36      {                                                                 
37         return getWeeklySalary();                                      
38      } // end method getPaymentAmount                                  
39
40      // return String representation of SalariedEmployee object
41      @Override
42      public String toString()
43      {
44         return String.format( "salaried employee: %s %s: $%,.2f",
45            super.toString(), "weekly salary", getWeeklySalary() );
46      } // end method toString
47   } // end class SalariedEmployee


Fig. G.26 | SalariedEmployee class that implements interface Payable method getPaymentAmount.

G.12.6 Using Interface Payable to Process Invoices and Employees Polymorphically

PayableInterfaceTest (Fig. G.27) illustrates that interface Payable can be used to process a set of Invoices and Employees polymorphically in a single application. Line 9 declares payableObjects and assigns it an array of four Payable variables. Lines 12–13 assign the references of Invoice objects to the first two elements of payableObjects. Lines 14–17 then assign the references of SalariedEmployee objects to the remaining two elements of payableObjects. These assignments are allowed because an Invoice is a Payable, a SalariedEmployee is an Employee and an Employee is a Payable. Lines 23–29 use the enhanced for statement to polymorphically process each Payable object in payableObjects, printing the object as a String, along with the payment amount due. Line 27 invokes method toString via a Payable interface reference, even though toString is not declared in interface Payableall references (including those of interface types) refer to objects that extend Object and therefore have a toString method. (Method toString also can be invoked implicitly here.) Line 28 invokes Payable method getPaymentAmount to obtain the payment amount for each object in payableObjects, regardless of the actual type of the object. The output reveals that the method calls in lines 27–28 invoke the appropriate class’s implementation of methods toString and getPaymentAmount. For instance, when currentPayable refers to an Invoice during the first iteration of the for loop, class Invoice’s toString and getPaymentAmount execute.


 1   // Fig. G.27: PayableInterfaceTest.java
 2   // Tests interface Payable.
 3
 4   public class PayableInterfaceTest
 5   {
 6      public static void main( String[] args )
 7      {
 8         // create four-element Payable array
 9         Payable[] payableObjects = new Payable[ 4 ];
10
11         // populate array with objects that implement Payable
12         payableObjects[ 0 ] = new Invoice( "01234", "seat", 2, 375.00 );
13         payableObjects[ 1 ] = new Invoice( "56789", "tire", 4, 79.95 );
14         payableObjects[ 2 ] =
15            new SalariedEmployee( "John", "Smith", "111-11-1111", 800.00 );
16         payableObjects[ 3 ] =
17            new SalariedEmployee( "Lisa", "Barnes", "888-88-8888", 1200.00 );
18
19         System.out.println(
20            "Invoices and Employees processed polymorphically: " );
21
22         // generically process each element in array payableObjects
23         for ( Payable currentPayable : payableObjects )
24         {
25            // output currentPayable and its appropriate payment amount
26            System.out.printf( "%s %s: $%,.2f ",
27               currentPayable.toString(),
28               "payment due", currentPayable.getPaymentAmount() );
29         } // end for
30      } // end main
31   } // end class PayableInterfaceTest


Invoices and Employees processed polymorphically:

invoice:
part number: 01234 (seat)
quantity: 2
price per item: $375.00
payment due: $750.00

invoice:
part number: 56789 (tire)
quantity: 4
price per item: $79.95
payment due: $319.80

salaried employee: John Smith
social security number: 111-11-1111
weekly salary: $800.00
payment due: $800.00

salaried employee: Lisa Barnes
social security number: 888-88-8888
weekly salary: $1,200.00
payment due: $1,200.00


Fig. G.27 | Payable interface test program processing Invoices and Employees polymorphically.

G.13 Common Interfaces of the Java API

In this section, we overview several common interfaces found in the Java API. The power and flexibility of interfaces is used frequently throughout the Java API. These interfaces are implemented and used in the same manner as the interfaces you create (e.g., interface Payable in Section G.12.2). The Java API’s interfaces enable you to use your own classes within the frameworks provided by Java, such as comparing objects of your own types and creating tasks that can execute concurrently with other tasks in the same program. Figure G.28 overviews a few commonly used interfaces of the Java API.

Image

Fig. G.28 | Common interfaces of the Java API.

G.14 Wrap-Up

We introduced inheritance—the ability to create classes by absorbing an existing class’s members and embellishing them with new capabilities. You learned the notions of superclasses and subclasses and used keyword extends to create a subclass that inherits members from a superclass. We showed how to use the @Override annotation to prevent unintended overloading by indicating that a method overrides a superclass method. We introduced the access modifier protected; subclass methods can directly access protected superclass members. You learned how to use super to access overridden superclass members. You also saw how constructors are used in inheritance hierarchies. Next, you learned about the methods of class Object, the direct or indirect superclass of all Java classes.

We discussed polymorphism—the ability to process objects that share the same superclass in a class hierarchy as if they’re all objects of the superclass. We considered how polymorphism makes systems extensible and maintainable, then demonstrated how to use overridden methods to effect polymorphic behavior. We introduced abstract classes, which allow you to provide an appropriate superclass from which other classes can inherit. You learned that an abstract class can declare abstract methods that each subclass must implement to become a concrete class and that a program can use variables of an abstract class to invoke the subclasses’ implementations of abstract methods polymorphically. You also learned how to determine an object’s type at execution time. We discussed the concepts of final methods and classes. Finally, we discussed declaring and implementing an interface as another way to achieve polymorphic behavior.

You should now be familiar with classes, objects, encapsulation, inheritance, polymorphism and interfaces—the most essential aspects of object-oriented programming.

Next, you’ll learn about exceptions, useful for handling errors during a program’s execution. Exception handling helps you build more robust programs.

Self-Review Exercises (Sections G.1G.5)

G.1 Fill in the blanks in each of the following statements:

a) __________ is a form of software reusability in which new classes acquire the members of existing classes and embellish those classes with new capabilities.

b) A superclass’s __________ members can be accessed in the superclass declaration and in subclass declarations.

c) In a(n) __________ relationship, an object of a subclass can also be treated as an object of its superclass.

d) In a(n) __________ relationship, a class object has references to objects of other classes as members.

e) In single inheritance, a class exists in a(n) __________ relationship with its subclasses.

f) A superclass’s __________ members are accessible anywhere that the program has a reference to an object of that superclass or to an object of one of its subclasses.

g) When an object of a subclass is instantiated, a superclass __________ is called implicitly or explicitly.

h) Subclass constructors can call superclass constructors via the __________ keyword.

G.2 State whether each of the following is true or false. If a statement is false, explain why.

a) Superclass constructors are not inherited by subclasses.

b) A has-a relationship is implemented via inheritance.

c) A Car class has an is-a relationship with the SteeringWheel and Brakes classes.

d) When a subclass redefines a superclass method by using the same signature, the subclass is said to overload that superclass method.

Self-Review Exercises (Sections G.6G.13)

G.3 Fill in the blanks in each of the following statements:

a) If a class contains at least one abstract method, it’s a(n) __________ class.

b) Classes from which objects can be instantiated are called __________ classes.

c) __________ involves using a superclass variable to invoke methods on superclass and subclass objects, enabling you to “program in the general.”

d) Methods that are not interface methods and that do not provide implementations must be declared using keyword __________.

e) Casting a reference stored in a superclass variable to a subclass type is called __________.

G.4 State whether each of the statements that follows is true or false. If false, explain why.

a) All methods in an abstract class must be declared as abstract methods.

b) Invoking a subclass-only method through a subclass variable is not allowed.

c) If a superclass declares an abstract method, a subclass must implement that method.

d) An object of a class that implements an interface may be thought of as an object of that interface type.

Answers to Self-Review Exercises (Sections G.1G.5)

G.1

a) Inheritance.

b) public and protected.

c) is-a or inheritance.

d) has-a or composition.

e) hierarchical.

f) public.

g) constructor.

h) super.

G.2

a) True.

b) False. A has-a relationship is implemented via composition. An is-a relationship is implemented via inheritance.

c) False. This is an example of a has-a relationship. Class Car has an is-a relationship with class Vehicle.

d) False. This is known as overriding, not overloading—an overloaded method has the same name, but a different signature.

Answers to Self-Review Exercises (Sections G.6G.13)

G.3

a) abstract.

b) concrete.

c) Polymorphism.

d) abstract.

e) downcasting.

G.4

a) False. An abstract class can include methods with implementations and abstract methods.

b) False. Trying to invoke a subclass-only method with a superclass variable is not allowed.

c) False. Only a concrete subclass must implement the method.

d) True.

Exercises (Sections G.1G.5)

G.5 Discuss the ways in which inheritance promotes software reuse, saves time during program development and helps prevent errors.

G.6 Draw an inheritance hierarchy for students at a university similar to the hierarchy shown in Fig. G.2. Use Student as the superclass of the hierarchy, then extend Student with classes UndergraduateStudent and GraduateStudent. Continue to extend the hierarchy as deep (i.e., as many levels) as possible. For example, Freshman, Sophomore, Junior and Senior might extend UndergraduateStudent, and DoctoralStudent and MastersStudent might be subclasses of GraduateStudent. After drawing the hierarchy, discuss the relationships that exist between the classes. [Note: You do not need to write any code for this exercise.]

G.7 Some programmers prefer not to use protected access, because they believe it breaks the encapsulation of the superclass. Discuss the relative merits of using protected access vs. using private access in superclasses.

G.8 Write an inheritance hierarchy for classes Quadrilateral, Trapezoid, Parallelogram, Rectangle and Square. Use Quadrilateral as the superclass of the hierarchy. Create and use a Point class to represent the points in each shape. Make the hierarchy as deep (i.e., as many levels) as possible. Specify the instance variables and methods for each class. The private instance variables of Quadrilateral should be the x-y coordinate pairs for the four endpoints of the Quadrilateral. Write a program that instantiates objects of your classes and outputs each object’s area (except Quadrilateral).

Exercises (Sections G.6G.13)

G.9 How does polymorphism enable you to program “in the general” rather than “in the specific”? Discuss the key advantages of programming “in the general.”

G.10 What are abstract methods? Describe the circumstances in which an abstract method would be appropriate.

G.11 How does polymorphism promote extensibility?

G.12 Discuss four ways in which you can assign superclass and subclass references to variables of superclass and subclass types.

G.13 Compare and contrast abstract classes and interfaces. Why would you use an abstract class? Why would you use an interface?

G.14 (Payroll System Modification) Modify the payroll system of Figs. G.16G.21 to include private instance variable birthDate in class Employee. Use class Date of Fig. F.7 to represent an employee’s birthday. Add get methods to class Date. Assume that payroll is processed once per month. Create an array of Employee variables to store references to the various employee objects. In a loop, calculate the payroll for each Employee (polymorphically), and add a $100.00 bonus to the person’s payroll amount if the current month is the one in which the Employee’s birthday occurs.

G.15 (Payroll System Modification) Modify the payroll system of Figs. G.16G.21 to include an additional Employee subclass PieceWorker that represents an employee whose pay is based on the number of pieces of merchandise produced. Class PieceWorker should contain private instance variables wage (to store the employee’s wage per piece) and pieces (to store the number of pieces produced). Provide a concrete implementation of method earnings in class PieceWorker that calculates the employee’s earnings by multiplying the number of pieces produced by the wage per piece. Create an array of Employee variables to store references to objects of each concrete class in the new Employee hierarchy. For each Employee, display its String representation and earnings.

G.16 (Accounts Payable System Modification) In this exercise, we modify the accounts payable application of Figs. G.23G.27 to include the complete functionality of the payroll application of Figs. G.16G.21. The application should still process two Invoice objects, but now should process one object of each of the four Employee subclasses. If the object currently being processed is a BasePlusCommissionEmployee, the application should increase the BasePlusCommissionEmployee’s base salary by 10%. Finally, the application should output the payment amount for each object. Complete the following steps to create the new application:

a) Modify classes HourlyEmployee (Fig. G.18) and CommissionEmployee (Fig. G.19) to place them in the Payable hierarchy as subclasses of the version of Employee (Fig. G.25) that implements Payable. [Hint: Change the name of method earnings to getPaymentAmount in each subclass so that the class satisfies its inherited contract with interface Payable.]

b) Modify class BasePlusCommissionEmployee (Fig. G.20) such that it extends the version of class CommissionEmployee created in part (a).

c) Modify PayableInterfaceTest (Fig. G.27) to polymorphically process two Invoices, one SalariedEmployee, one HourlyEmployee, one CommissionEmployee and one BasePlusCommissionEmployee. First output a String representation of each Payable object. Next, if an object is a BasePlusCommissionEmployee, increase its base salary by 10%. Finally, output the payment amount for each Payable object.

G.17 (Accounts Payable System Modification) It’s possible to include the functionality of the payroll application (Figs. G.16G.21) in the accounts payable application without modifying Employee subclasses SalariedEmployee, HourlyEmployee, CommissionEmployee or BasePlusCommissionEmplyee. To do so, you can modify class Employee (Fig. G.16) to implement interface Payable and declare method getPaymentAmount to invoke method earnings. Method getPaymentAmount would then be inherited by the subclasses in the Employee hierarchy. When getPaymentAmount is called for a particular subclass object, it polymorphically invokes the appropriate earnings method for that subclass. Reimplement Exercise G.16 using the original Employee hierarchy from the payroll application of Figs. G.16G.21. Modify class Employee as described in this exercise, and do not modify any of class Employee’s subclasses.

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

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