Java Polymorphism

In this post, I will talk about the concepts and benefits of polymorphism. Then, I will point out some important properties about using class variables and methods when polymorphism happens. Moving on, object down casting and ‘instanceof’ method will be briefly introduced. Finally, I will demonstrate a case study combining the knowledge of using interface and polymorphism.

What is polymorphism?

‘Extends’ and ‘implements’ are prerequisites of polymorphism which lets one object have multiple forms. (E.g. Jason can not only be a student but also be a human.)


Polymorphism in code

// (Base class) className = new (Derived class());
// (Base interface) className = new (Derived interface());

Benefits of polymorphism

No matter what type of objects we new on the right side, we do not need to change the method that we want to invoke.

// Without Polymorphism
Teacher one = new Teacher();
one.teach();
Tutor two = new Tutor();
two.tutor();

// With Polymorphism
Employee one = new Teacher();
one.work();
Employee two = new Tutor();
two.work();

Class variables in polymorphism

1: Give a higher priority to use the member variable from the object on the left side of the ‘=’. If the complier cannot find the corresponding variable, then it will find this variable upwards.

2: Check left side when compiling, check left side when running.

3: If we use class method to access class variable indirectly, the compiler checks which class contains this method and then use it.

// Base class
public class Parent {
    int num = 10;

    public void showNum() {
        System.out.println(num)
    }
}

// Derived class
public class Child extends Parent {
    int num = 20;
    int age = 21;
}

// Class variables in polymorphism
public class Demo {
    public static void main(String[] args) {
        Parent obj = new Child();

        System.out.println(obj.age); // Wrong! The object on the left side is 'Parent' type, however, there is no variable called 'age'. [Rule 1]

        // This will print 10
        System.out.println(obj.num); // Based on the rule of 'check left side when compiling, check left side when running.', we can see that the left side is 'Parent', so this will use the the member variable 'num' from the 'Parent' class. [Rule 2]

        obj.showNum(); // The method belongs to the 'Parent' class, therefore, it prints '10'; If we override it on the 'Child' class, then it will print '20'. (We can also use Rule 2 in the 'Class methods in polymorphism' section to explain it.)
    }
}

Class methods in polymorphism

1: Give a higher priority to invoke the method from the object that has been new-ed. If the complier cannot find the corresponding method, then it will find this method upwards.

2: Check left side when compiling, check right side when running.

public class Demo {
    public static void main(String[] args) {
        Parent obj = new Child(); // Polymorphism

        obj.methodCommon(); // Based on the rule of 'check right side when running', we can see that the right side is 'Child', so the 'methodCommon' from the child class is invoked with higher priority.

        obj.methodParent(); // Child class does not have this method, then we check this method upwards.

        // obj.methodChild(); // Wrong! Compiling error.
        // Based on the rule of 'Check left side when compiling', we can see that the left side is 'Parent', but this class does not have 'methodChild' method.
    }
}

Object Down Casting

Usage: Convert the base object back to the original derived object. It is similar to:

public class Demo {
    public static void main(String[] args) {
        Animal animal = new Cat();
        animal.eat();
        // animal.catchMouse();  (Wrong!)

        // Correct Object down casting
        Cat cat = (Cat) animal;
        cat.catchMouse();

        // Wrong object down casting
        // ClassCastException
        // Dog dog = (Dog) animal;
    }
}

Use ‘instanceof’ to check the type of the object

Issue: Sometimes, we do not know if the ‘animal’ object points to ‘Cat’ object or ‘Dog’ object. Therefore, we need a method to know the type that the base object refers to.

public class Demo {
    public static void main(String[] args) {
        // If your girl friend wants any animal to be her pet.
        giveMeAPet(new Dog());
    }

    // An example
    public static void giveMeAPet(Animal animal) {
        // We can use 'instanceof' to safely down cast the object
        // Check if the original derived class is 'Dog' or not
        if (animal instanceof Dog) {
            Dog dog = (Dog) animal;
            dog.watchHouse();
        }

        // Check if the original derived class is 'Cat' or not
        if (animal instanceof Cat) {
            Cat cat = (Cat) animal;
            cat.catchMouse();
        }
    }
}

Interface and polymorphism case study

Describe ‘Laptop’ class which implements ‘USB mouse’ and ‘USB keyboard’ inherited from ‘USB’ interface.

  • USB interface: It includes the functionality of power on and power off USB devices (Mouse and Keyboard).
  • Laptop class: It includes the functionality of power on and power off itself as well as using USB equipment.
  • Mouse class: It implements USB interface and can click.
  • Keyboard class: It implements USB interface and can knock on.

Define the interface USB

public interface USB {
    public abstract void open();
    public abstract void close();
}

Use the interface

public class Mouse implements USB{
    @Override
    public void open() {
        System.out.println("The mouse is opened");
    }

    @Override
    public void close() {
        System.out.println("The mouse is closed");
    }

    public void click() {
        System.out.println("Click");
    }
}

public class Keyboard implements USB{
    @Override
    public void open() {
        System.out.println("The keyboard is opened");
    }

    @Override
    public void close() {
        System.out.println("The keyboard is closed");
    }

    public void type() {
        System.out.println("Knock on");
    }
}

Define the laptop class

public class Laptop {
    public void powerOn() {
        System.out.println("Power on the laptop");
    }

    public void powerOff() {
        System.out.println("Power off the laptop");
    }

    // Use USB device
    public void useDevice(USB myUSB) {
        myUSB.open();
        if (myUSB instanceof Mouse) {
            Mouse mouse = (Mouse) myUSB;
            mouse.click();
        } else if (myUSB instanceof Keyboard) {
            Keyboard keyboard = (Keyboard) myUSB;
            keyboard.type();
        }
        myUSB.close();
    }
}

Simulate the process

public class Demo {
    public static void main(String[] args) {
        // Create a laptop
        Laptop laptop = new Laptop();
        laptop.powerOn();

        // Prepare a mouse and use it
        USB usbMouse = new Mouse();
        laptop.useDevice(usbMouse);

        // Prepare a keyboard and use it
        Keyboard usbKeyboard = new Keyboard();
        laptop.useDevice(usbKeyboard); // Still correct, automatically execute object up casting (implementing class -> interface)

        // Power off the laptop
        laptop.powerOff();
    }
}

Program Output

Power on the laptop
The mouse is opened
Click
The mouse is closed
The keyboard is opened
Knock on
The keyboard is closed
Power off the laptop