I'm reading up a book on Java and currently on the Polymorphism
topic as well as how to downcast
a reference variable. However, I'm pretty stuck with understanding the concept of downcasting. Below is the uml for the example I'm following.
For all objects of BasePlusCommissionEmployee
they are to given a 10% increase on their base salary. The other Employee
subclasses are as per normal. The PayrollSystemTest
contains the main method to run the application.
// Fig. 10.9: PayrollSystemTest.java
// Employee hierarchy test program.
public class PayrollSystemTest
{
public static void main(String[] args)
{
// create subclass objects
SalariedEmployee salariedEmployee =
new SalariedEmployee("John", "Smith", "111-11-1111", 800.00);
HourlyEmployee hourlyEmployee =
new HourlyEmployee("Karen", "Price", "222-22-2222", 16.75, 40.0);
CommissionEmployee commissionEmployee =
new CommissionEmployee(
"Sue", "Jones", "333-33-3333", 10000, .06);
BasePlusCommissionEmployee basePlusCommissionEmployee =
new BasePlusCommissionEmployee(
"Bob", "Lewis", "444-44-4444", 5000, .04, 300);
System.out.println("Employee processed individually:");
System.out.printf("%n%s%n%s: $%,.2f%n%n",
salariedEmployee, "earned", salariedEmployee.earnings());
System.out.printf("%s%n%s: $%,.2f%n%n",
hourlyEmployee, "earned", hourlyEmployee.earnings());
System.out.printf("%s%n%s: $%,.2f%n%n",
commissionEmployee, "earned", commissionEmployee.earnings());
System.out.printf("%s%n%s: $%,.2f%n%n",
basePlusCommissionEmployee,
"earned", basePlusCommissionEmployee.earnings());
// create four-element Employee array
Employee[] employees = new Employee[4];
// initialize array with Employees
employees[0] = salariedEmployee;
employees[1] = hourlyEmployee;
employees[2] = commissionEmployee;
employees[3] = basePlusCommissionEmployee;
System.out.printf("Employees processed polymorphically:%n%n");
// generically process each element in array employees
for (Employee currentEmployee : employees)
{
System.out.println(currentEmployee); // invokes toString
// determine whether element is a BasePlusCommissionEmployee
if (currentEmployee instanceof BasePlusCommissionEmployee)
{
// downcast Employee reference to
// BasePlusCommissionEmployee reference
BasePlusCommissionEmployee employee =
(BasePlusCommissionEmployee) currentEmployee;
employee.setBaseSalary(1.10 * employee.getBaseSalary());
System.out.printf(
"new base salary with 10%% increase is: $%,.2f%n",
employee.getBaseSalary());
} // end if
System.out.printf(
"earned $%,.2f%n%n", currentEmployee.earnings());
} // end for
// get type name of each object in employees array
for (int j = 0; j < employees.length; j++)
System.out.printf("Employee %d is a %s%n", j,
employees[j].getClass().getName());
} // end main
} // end class PayrollSystemTest
The book further explains that the enhanced for loop iterates the array employees
and invokes methods toString
and earnings
with Employee
variable currentEmployee
which is assigned the reference to a different Employee
in the array on each iteration. As a result, the output illustrates that the specific methods for each class are invoked and are resolved at execution time, based on the type of the object.
In order to invoke BasePlusCommissionEmployee
's methods getBaseSalary
and setBaseSalary
on the current Employee
object, a condition statement is used to check if the object reference is a BasePlusCommissionEmployee
object by using the instanceof operator and if the condition is true the object has to be downcast from Employee
to BasePlusCommissionEmployee
type before invoking the methods mentioned.
This seriously confuses me because we are able to access the subclasses' toString
method but has to downcast the object in order to use the other methods namely getBaseSalary
and setBaseSalary
? Why is this the case?
Because toString()
is defined in Object
, therefore available in every class. The getter and setter for base salary is available only in BasePlusCommissionEmployee
, so you can't call it through an Employee
reference (what would happen if it was referring to a different type?).
This example is not something you'd see in real code. Using instanceof to determine what to do is bad style.
When you want invoke a method on an instance, the methods you can call depends on multiple things, among the declared type of the instance, the modifier of the method and from where you call them.
In your example, it is the declared type of the instance which should interest you.
For example when you declare this :
String s = new String("string");
You can call accessible methods from the String
class.
For example :
s.toString();
s.trim();
etc...
In your case, when you declare this :
BasePlusCommissionEmployee basePlusCommissionEmployee =
new BasePlusCommissionEmployee(
"Bob", "Lewis", "444-44-4444", 5000, .04, 300);
Employee currentEmployee = basePlusCommissionEmployee;
you can do :
basePlusCommissionEmployee.getBaseSalary()
because the BasePlusCommissionEmployee
declared type provides the method for.
you can also do :
basePlusCommissionEmployee.toString()
and currentEmployee.toString()
because both types (Employee
and BasePlusCommissionEmployee
) provide the method for toString()
because the method is a public method from Object
class and all classes inherit from Object
class, so these class have the toString()
method.
But you cannot do :
currentEmployee.getBaseSalary()
because the Employee
declared type doesn't provide the method for.
To bypass it, you can downcast from the base class to the target child class :
Employee currentEmployee = basePlusCommissionEmployee;
((BasePlusCommissionEmployee)currentEmployee).getBaseSalary();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With