Demonstrating Polymorphic Behavior

Section 9.4 created a commission employee class hierarchy, in which class BasePlusCommissionEmployee inherited from class CommissionEmployee. The examples in that section manipulated CommissionEmployee and BasePlusCommissionEmployee objects by using references to them to invoke their methods. We aimed superclass references at superclass objects and subclass references at subclass objects. These assignments are natural and straightforwardsuperclass references are intended to refer to superclass objects, and subclass references are intended to refer to subclass objects. However, as you will soon see, other assignments are possible.

In the next example, we aim a superclass reference at a subclass object. We then show how invoking a method on a subclass object via a superclass reference invokes the subclass functionalitythe type of the actual referenced object, not the type of the reference, determines which method is called. This example demonstrates the key concept that an object of a subclass can be treated as an object of its superclass. This enables various interesting manipulations. A program can create an array of superclass references that refer to objects of many subclass types. This is allowed because each subclass object is an object of its superclass. For instance, we can assign the reference of a BasePlusCommissionEmployee object to a superclass CommissionEmployee variable because a BasePlusCommissionEmployee is a CommissionEmployeewe can treat a BasePlusCommissionEmployee as a CommissionEmployee.

As you will learn later in the chapter, we cannot treat a superclass object as a subclass object because a superclass object is not an object of any of its subclasses. For example, we cannot assign the reference of a CommissionEmployee object to a subclass BasePlusCommissionEmployee variable because a CommissionEmployee is not a BasePlusCommissionEmployeea CommissionEmployee does not have a baseSalary instance variable and does not have methods setBaseSalary and getBaseSalary. The is-a relationship applies only from a subclass to its direct (and indirect) superclasses, and not vice versa.

It turns out that the Java compiler does allow the assignment of a superclass reference to a subclass variable if we explicitly cast the superclass reference to the subclass typea technique we discuss in greater detail in Section 10.5. Why would we ever want to perform such an assignment? A superclass reference can be used to invoke only the methods declared in the superclassattempting to invoke subclass-only methods through a superclass reference results in compilation errors. If a program needs to perform a subclass-specific operation on a subclass object referenced by a superclass variable, the program must first cast the superclass reference to a subclass reference through a technique known as downcasting. This enables the program to invoke subclass methods that are not in the superclass. We will show you a concrete example of downcasting in Section 10.5.

The example in Fig. 10.1 demonstrates three ways to use superclass and subclass variables to store references to superclass and subclass objects. The first two are straightforwardas in Section 9.4, we assign a superclass reference to a superclass variable, and we assign a subclass reference to a subclass variable. Then we demonstrate the relationship between subclasses and superclasses (i.e., the is-a relationship) by assigning a subclass reference to a superclass variable. [Note: This program uses classes CommissionEmployee3 and BasePlusCommissionEmployee4 from Fig. 9.12 and Fig. 9.13, respectively.]

Figure 10.1. Assigning superclass and subclass references to superclass and subclass variables.

(This item is displayed on pages 464 - 465 in the print version)

1 // Fig. 10.1: PolymorphismTest.java 2 // Assigning superclass and subclass references to superclass and 3 // subclass variables. 4 5 public class PolymorphismTest 6 { 7 public static void main( String args[] ) 8 { 9 // assign superclass reference to superclass variable 10 CommissionEmployee3 commissionEmployee = new CommissionEmployee3( 11 "Sue", "Jones", "222-22-2222", 10000, .06 ); 12 13 // assign subclass reference to subclass variable 14 BasePlusCommissionEmployee4 basePlusCommissionEmployee = 15 new BasePlusCommissionEmployee4( 16 "Bob", "Lewis", "333-33-3333", 5000, .04, 300 ); 17 18 // invoke toString on superclass object using superclass variable 19 System.out.printf( "%s %s: %s ", 20 "Call CommissionEmployee3's toString with superclass reference ", 21 "to superclass object", commissionEmployee.toString() ); 22 23 // invoke toString on subclass object using subclass variable 24 System.out.printf( "%s %s: %s ", 25 "Call BasePlusCommissionEmployee4's toString with subclass", 26 "reference to subclass object", 27 basePlusCommissionEmployee.toString() ); 28 29 // invoke toString on subclass object using superclass variable 30 CommissionEmployee3 commissionEmployee2 = 31 basePlusCommissionEmployee; 32 System.out.printf( "%s %s: %s ", 33 "Call BasePlusCommissionEmployee4's toString with superclass", 34 "reference to subclass object", commissionEmployee2.toString() ); 35 } // end main 36 } // end class PolymorphismTest  

Call CommissionEmployee3's toString with superclass reference to superclass object: commission employee: Sue Jones social security number: 222-22-2222 gross sales: 10000.00 commission rate: 0.06 Call BasePlusCommissionEmployee4's toString with subclass reference to subclass object: base-salaried commission employee: Bob Lewis social security number: 333-33-3333 gross sales: 5000.00 commission rate: 0.04 base salary: 300.00 Call BasePlusCommissionEmployee4's toString with superclass reference to subclass object: base-salaried commission employee: Bob Lewis social security number: 333-33-3333 gross sales: 5000.00 commission rate: 0.04 base salary: 300.00  

In Fig. 10.1, lines 1011 create a CommissionEmployee3 object and assign its reference to a CommissionEmployee3 variable. Lines 1416 create a BasePlusCommissionEmployee4 object and assign its reference to a BasePlusCommissionEmployee4 variable. These assignments are naturalfor example, a CommissionEmployee3 variable's primary purpose is to hold a reference to a CommissionEmployee3 object. Lines 1921 use reference commissionEmployee to invoke toString explicitly. Because commissionEmployee refers to a CommissionEmployee3 object, superclass CommissionEmployee3's version of toString is called. Similarly, lines 2427 use basePlusCommissionEmployee to invoke toString explicitly on the BasePlusCommissionEmployee4 object. This invokes subclass BasePlusCommissionEmployee4's version of toString.

Lines 3031 then assign the reference to subclass object basePlusCommissionEmployee to a superclass CommissionEmployee3 variable, which lines 3234 use to invoke method toString. A superclass variable that contains a reference to a subclass object and is used to call a method actually calls the subclass version of the method. Hence, commissionEmployee2.toString() in line 34 actually calls class BasePlusCommissionEmployee4's toString method. The Java compiler allows this "crossover" because an object of a subclass is an object of its superclass (but not vice versa). When the compiler encounters a method call made through a variable, the compiler determines if the method can be called by checking the variable's class type. If that class contains the proper method declaration (or inherits one), the compiler allows the call to be compiled. At execution time, the type of the object to which the variable refers determines the actual method to use.

Категории