Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Polymorphism and downcasting questions

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?

like image 400
Scorpiorian83 Avatar asked Oct 15 '25 04:10

Scorpiorian83


2 Answers

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.

like image 199
Kayaman Avatar answered Oct 16 '25 16:10

Kayaman


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();
like image 43
davidxxx Avatar answered Oct 16 '25 16:10

davidxxx



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!