JavaObject-oriented programmingInheritance and polymorphism

Polymorphism

Kinds of polymorphism

In the general case, polymorphism means that something (an object or another entity) has many forms.

Java provides two types of polymorphism: static (compile-time) and dynamic (run-time) polymorphism. The first one is achieved by method overloading, the second one based on inheritance and method overriding.

The more theoretical approach subdivides polymorphism into several fundamentally different types:

  • Ad-hoc polymorphism refers to polymorphic functions that can be applied to arguments of different types, but that behave differently depending on the type of the argument to which they are applied. Java supports it as method overloading.
  • Subtype polymorphism (also known as subtyping) is a possibility to use an instance of a subclass when an instance of the base class is permitted.
  • Parametric polymorphism: when a code is written without mention of any specific type and thus can be used transparently with any number of new types. Java supports it as generics or generic programming.

In this topic, we'll consider only run-time polymorphism that is widely used in the object-oriented programming.

Runtime polymorphic behavior

We remind you, method overriding it is when a subclass redefines a method of the superclass with the same name.

The run-time polymorphism relies on two principles:

  • a reference variable of a superclass can refer to any subtype objects;
  • a superclass method can be overridden in a subclass.

It works when an overridden method is calling through the reference variable of a superclass. Java determines at runtime which version (superclass/subclasses) of the method is to be executed based on the type of the object being referred, not the type of reference. It uses the mechanism known as dynamic method dispatching.

Example. Here, a class hierarchy is presented. The superclass MythicalAnimal has two subclasses: Chimera and Dragon. The base class has the method hello. Both subclasses override the method.

class MythicalAnimal {    

    public void hello() {
        System.out.println("Hello, I'm an unknown animal");
    }
}

class Chimera extends MythicalAnimal {

    public void hello() {
        System.out.println("Hello! Hello!");
    }
}

class Dragon extends MythicalAnimal {

    public void hello() {
        System.out.println("Rrrr...");
    }
}

We can create references to the class MythicalAnimal and assign it to subclass objects.

MythicalAnimal chimera = new Chimera();
MythicalAnimal dragon = new Dragon();
MythicalAnimal animal = new MythicalAnimal();

And invoke overridden methods through the base class references:

animal.hello(); // Hello, i'm an unknown animal
chimera.hello(); // Hello! Hello!
dragon.hello(); // Rrrr...

So, the result of a method call depends on the actual type of an instance, not a reference. It's a polymorphic feature in Java. The JVM calls the appropriate method for the object that is referred to in each variable.

It allows a class to specify methods that will be common to all of its subclasses while allowing subclasses to define the specific implementation of some or all of those methods. It's very useful for object-oriented design, especially, together with abstract methods and interfaces which are studying in other topics.

Polymorphism within a class hierarchy

The same thing works with methods that are used only within a hierarchy and not accessible from outside.

Example. There is a hierarchy of files. The parent class File represents a description of a single file in the file system. It has a subclass named ImageFile. It overrides the method getFileInfo of the parent class.
class File {

    protected String fullName;

    // constructor with a single paratemer

    // getters and setters

    public void printFileInfo() {
        String info = this.getFileInfo(); // here is polymorphic behaviour!!!
        System.out.println(info);
    }

    protected String getFileInfo() {
        return "File: " + fullName;
    }
}

class ImageFile extends File {

    protected int width;
    protected int height;
    protected byte[] content;

    // constructor

    // getters and setters

    @Override
    protected String getFileInfo() {
        return String.format("Image: %s, width: %d, height: %d", fullName, height, width); 
    }
}

The parent class has the public method printFileInfo and the protected method getFileInfo. The second method is overridden in the subclass, but the subclass doesn't override the first method.

Let's create an instance of ImageFile and assign it to the variable of File.

File img = new ImageFile("/path/to/file/img.png", 640, 480, someBytes); // assigning an object

Now, when we call the method printFileInfo, it invokes the overridden version of the method getFileInfo.

img.printFileInfo(); // It prints "Image: /path/to/file/img.png, width: 480, height: 640"

So, run-time polymorphism allows you to invoke an instance overridden method of a subclass having a reference to a base class.
How did you like the theory?
Report a typo