4

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.

UML

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?

Scorpiorian83
  • 469
  • 1
  • 4
  • 17

3 Answers3

3

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.

Kayaman
  • 72,141
  • 5
  • 83
  • 121
  • yup what you have said makes so much sense to me. by the way, why would you say using instanceof is a bad practice? – Scorpiorian83 Dec 14 '16 at 16:07
  • instanceof is bad practice because it usually is replaceable by code applying polymorphism. See GhostCat's answer. To put it another way: the code `if (obj instanceof ClassA) { doA(); } else if (obj instanceof ClassB) { doB(); }` is better written by defining the method `doStuff()` on the superclass of ClassA and ClassB, overriding it in each class to do the right thing, then replacing the code above with `obj.doStuff()`. – Paul Brinkley Dec 14 '16 at 17:53
2

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();
davidxxx
  • 125,838
  • 23
  • 214
  • 215
  • thank you David for taking your precious time to explain to me. I finally understand why but as for the last line of code you showed me, it's my first time seeing it. May I ask what does it mean? Is there a term for this? – Scorpiorian83 Dec 14 '16 at 16:14
  • You are welcome. Great you understood :) The last line is a shortcut when you want to cast a object to call on it a method but you don't need to store the result of the cast in an intermediary variable . It is maybe the global parenthesis which disturb you. it allows to apply the `getBaseSalary()` method on the `BasePlusCommissionEmployee` typed instance. If you write only `(BasePlusCommissionEmployee) currentEmployee.getBaseSalary()` you would have a compilation error because `getBaseSalary()` will be applied to `currentEmployee` variable. – davidxxx Dec 14 '16 at 16:27
1

Worth adding in my eyes: this is actually a rather bad example.

A downcast is very often an indication of a bad design; and this here is nicely proving that rule.

That Payroll system should not need such an "instanceof" check; to then do some specific computations for a specific subclass.

That is the whole point of using polymorphism: those different classes BasePlusCommissionEmployee and CommissionEmployee should each contain the means to correctly compute the correct salary.

The thing to uphold here would be the TDA (tell dont ask) principle. Good OO is about telling some object "do the thing"; instead of asking the object about something, to then make an external decision based on that inner state (or nature in this case) of that object!

For anybody interested in understand how to really solve this problem, I suggest to look into "Agile Principles" by Robert Martin. That book describes design/implementation of a real world payroll system ...

GhostCat
  • 137,827
  • 25
  • 176
  • 248
  • thank you very much for your knowledge and I will also go get Agile Principles and have a good look at it cheers. – Scorpiorian83 Dec 14 '16 at 16:11
  • @Scorpiorian83 Hint: you can find the C# version of that book for free ... and as the source code is kinda less important in there (the UML diagrams are sure language independent) ... that is what I read first. – GhostCat Dec 14 '16 at 16:14
  • thanks again I just finished downloading the book. :D – Scorpiorian83 Dec 14 '16 at 16:25