PolymorphismS2C Home « Polymorphism

In this lesson we continue our investigation of OO concepts by learning about polymorphism, which is formed from the Greek roots of the terms poly meaning many and morph meaning change or form, which roughly translated means the ability to assume different forms. So what does this mean for us Java programmers? To explain this we will refresh our memory about how we create an object:

object creation
Figure 1. Typical object creation.

So lets go through the three step process above:

  1. Declaration - Here. we declare a reference type named garbage of type Garbage and the JVM allocates space for it.
  2. Creation - Tells the JVM to allocate space on The Heap for a new Garbage object.
  3. Assignment - Assign the new Garbage object to the reference type garbage.

The reference type and the object type are the same.

Polymorphic Method Invocation Top

With polymorphism when you declare the reference type in the declaration, it can be from a superclass of the object we are creating. Or looking from a different perspective, when creating an object, we can use any supertype from a class that we have extended. The compiler has no problem with this but the power of polymorphism happens at runtime through the concept of virtual method invocation. This concept is the dynamic selection of overridden methods at runtime based on the actual object type, rather than the reference type. This only applies to instance methods, everything else uses the reference type at runtime.

object creation
Figure 2. Polymorphic object creation.

So what does this give us I hear you ask? Well we can use the supertype for reference type declaration when instantiating our objects, safe in the knowledge that the JVM will use the actual object created to invoke the correct overridden methods of the subtypes at runtime. This ties in with the concept of abstraction that we looked at in the last lesson. By making generic methods abstract in the superclass we can be assured that the overridden method implementations are in place in the subclass at runtime. If you remember we have to implement all abstract methods from the superclass in the first concrete subclass. We will use an array to illustrate polymorphic method invocation:


package com.server2client;
/*
 Polymorphic Array 
*/ 
public class PolymorphicArray {

    public static void main (String[] args) {
        Truck[] truck = new Truck[2];  // Array declaration
        truck[0] = new HGV(); 
        truck[1] = new Garbage();
        for (int i=0; i < truck.length; i++) {
            truck[i].load("stuff");
        } 
    }
}

Save, compile and run the file in directory   c:\_OOConcepts in the usual way.

run Polymorphic Array
Screenshot 1. Running the PolymorphicArray class.

The above screenshot shows the output of running our PolymorphicArray class. The JVM uses virtual method invocation to dynamically invoke the correct subtype override. The overrides in the HGV and Garbage classes are shown below:


package com.server2client;
public class HGV extends Truck {
    ...
    public void load(String payload) {
        System.out.println("We are transporting " + payload + " in our Heavy Goods Vehicle." );
    }
}


package com.server2client;
public class Garbage extends Truck {
    ...
    public void load(String payload) {
        System.out.println("Our garbage truck eats " + payload);
    }
}

We can happily include new subtypes in the knowledge that any method overrides will be called correctly on that subtype.

Polymorphic Arguments Top

We can also make the arguments to our methods polymorphic. We will write a new class and a test to see this in action:


package com.server2client;
/*
  A Transit Class
*/ 
public class Transit {
    private int capacity = 4;

    public void hold(Vehicle v) {
        v.carry(capacity); 
    }
}

Save and compile the file in directory   c:\_OOConcepts in the usual way.

Here's our test class .


package com.server2client;
/*
 Test class for polymorphic arguments
*/ 
public class TestPolyArgs {

    public static void main (String[] args) {
        Vehicle bus = new Bus();  
        Vehicle hgv = new HGV();  
        Transit transit = new Transit(); 
        transit.hold(bus);
        transit.hold(hgv);
    }
}

run Polymorphic Arguments
Screenshot 2. Running the TestPolyArgs class.

The above screenshot shows the output of running our TestPolyArgs test class. As you can see we are passing the Vehicle supertype as an argumment to the hold() method. The JVM uses virtual method invocation to dynamically invoke the correct Carry() method based on the actual object type at runtime.

Polymorphic Return Types Top

Lastly we will look at using polymorphic return types. We will write a new test class to see this in action:


package com.server2client;
/*
 Test class for polymorphic return
*/ 
public class PolymorphicReturn {

    public static void main (String[] args) {
        // Array declaration below is creating an Array object so can use Abstract supertype
        Vehicle[] vehicle = new Vehicle[4];  
        for (int i=0; i < 4; i++) {
            vehicle[i] = createVehicle(i);
            vehicle[i].carry(i);  
        } 
    }
    /*
      Create a Vehicle subtype and return the Vehicle supertype
    */ 
    static Vehicle createVehicle (int i) {
        Vehicle vehicle;
      switch (i) {
            case 0:
                vehicle = new Car();
                break;
            case 1:
                vehicle = new HGV();
                break;
            case 2:
                vehicle = new Bus();
                break;
            default:
                vehicle = new Garbage();
      }
        return vehicle;
    }
}

run Polymorphic Return
Screenshot 3. Running the TestPolyArgs class.

The above screenshot shows the output of running our slightly contrived PolymorphicReturn test class. We are returning the Vehicle supertype from the createVehicle() method. The JVM uses virtual method invocation to dynamically invoke the correct Carry() method based on the actual object type on return.

We will return to polymorphism when we look at polymorphism and interfaces in the Deeper Into Polymorphism lesson later in the section.

Static Overrides? Top

I know what you're thinking, what on earth is something about statics doing in a lesson on polymorphism. Well, now we have talked about how polymorphism works I just want to hammer home the point that it only relates to overridden instance methods and nothing else. I also wanted to talk about method hiding which relates to static methods in a subclass with the same identifier in the superclass. To look at this we first had to get our heads around the idea of virtual method invocation and how this works with supertypes and subtypes. Now that is done we can use the same principle to look at method hiding:


package com.server2client;
/*
 Superclass to illustrate method hiding
*/ 
public class M {

    public static void aClassMethod() {
        System.out.println("The class method in M.");
    }
    public void anInstanceMethod() {
        System.out.println("The instance method in M.");
    }
}


package com.server2client;
/*
 Subclass to illustrate method hiding
*/ 
public class N extends M {

    public static void aClassMethod() {
        System.out.println("The class method in N.");
    }
    public void anInstanceMethod() {
        System.out.println("The instance method in N.");
    }
}


package com.server2client;
/*
 Test Class to illustrate method hiding
*/ 
public class TestHiding {

    public static void main(String[] args) {
        N n = new N();
        M m = n;
        M.aClassMethod();
        m.anInstanceMethod();
    }
}

In the above code the aClassMethod() static method in class N hides the aClassMethod() static method in class M. The anInstanceMethod() instance method in class N overrides the anInstanceMethod() instance method in class M.

run TestHiding class
Screenshot 4. Running the TestHiding class.

The above screenshot shows the output of running our TestHiding test class after compiling classes M and N. As you can see the aClassMethod() static method from the superclass gets invoked via the class name. The overridden anInstanceMethod() instance method from the subclass gets invoked via the object type.

Related Quiz

OO Concepts Quiz 9 - Polymorphism

Lesson 9 Complete

We continued our investigation of OO concepts by learning about polymorphism.

What's Next?

The next lesson looks at interfaces and the contracts we provide when using them.