Polymorphism in Java with Example Program


Before I explain polymorphism, you must understand what is generalization and specialization in context of object oriented design. Assume following classes exist in an inheritance hierarchy:

Object
LivingBeing
Animal
Pet
Cat

Generalization and Specialization 

Assume classes are defined in a way such that, Cat extends Pet, Pet extends Animal and so on ... upto Object. You see, classes are upper side of inheritance hierarchy refers to more generic concepts as compared to classes in bottom side of hierarchy, hence the term generalization. If we look at classes defined in lower side of the hierarchy, these are more specific entities. So we say, Cat is more specific entity compared to Pet (or Pet is generic and Cat is specific). In same way, Pet is more specific compared to Animal, Animal is specific compared to LivingBeing. So, if we view classes from top to bottom of the hierarchy, we move from generalization to specialization (or from generic to specific entity). Generalization is also called Abstraction, because more we move at top of hierarchy, our entities/concept become more abstract.

When choosing type of a variable (e.g. in method parameters or instance variables), we prefer to use generic classes rather specific, as long as they can serve the purpose. Polymorphism help to program in "general" rather writing program with "specific" classes. This approach of designing software makes it more extensible and also reduces to code size substantially. 

Variable Type and Object Type

Below I frequently refer to two terms i.e. Variable Type and Object Type. Its time to understand what are these and how they differ. Consider below code: (assume Rectangle class is child class of Shape)

Shape s  = new Rectangle();

Here 'new' operator creates an object of Rectangle and the reference of newly created object is stored in a variable 's', of type Shape. Note that, a variable of parent class can hold reference of parent class object and any of its child class object. So the above code is valid because Rectangle is child class of the Shape. Lets see code of Shape, Circle and Rectangle classes before we move on:

package com.bitspedia.inheritance2;

public class Shape {
    public double calculateArea() {
        return 0;
    }
}

package com.bitspedia.inheritance2;

public class Circle extends Shape {
    private int radius;

    public Circle(int radius) {
        this.radius = radius;
    }

    public int getRadius() {
        return radius;
    }

    public void setRadius(int radius) {
        this.radius = radius;
    }

    @Override
    public double calculateArea() {
        return Math.PI * (radius * radius);
    }
}

package com.bitspedia.inheritance2;

    public class Rectangle extends Shape {
        private int width;
        private int length;

        public Rectangle(int width, int length) {
            this.width = width;
            this.length = length;
        }

        @Override
        public double calculateArea() {
            int area = width * length;
            return area;
        }

        public int getWidth() {
            return width;
        }

        public void setWidth(int width) {
            this.width = width;
        }

        public int getLength() {
            return length;
        }

        public void setLength(int length) {
            this.length = length;
        }
    }

You see, calculateArea() is defined in Shape class and redefined in Rectangble and Circle classes. Each subclass redefine the method by writing logic to calculate area that makes sense in that specific subclass. Redefining an inheritend method in subclasses is called method overriding.

Consider the below code of ShapeTest class:

package com.bitspedia.inheritance2;

public class ShapeTest {

    public static void main(String[] args) {
        Shape shape = new Rectangle(2, 3);
        double area = shape.calculateArea();
        System.out.println(area);
    }
}

Line 6-7 are important, lets discuss each one by one:
Shape s = new Rectanlge(2,3);
This statement is valid. As we discussed earlier, a variable of parent class can store reference of child class object, here Shape is parent and Rectangle is child class. 

Now lets see the second line i.e.:
double area = s.calculateArea();
There are two very important concepts being used her that you must understand before moving ahead.

1. Is the calculateArea() method call valid when we make it from parent class variable type i.e. 's'
2. Type of variable is Shape and type of object is Rectangle. calculateArea() method exist in both classes, which version would be called in above case?

When we call a method, at compile time, the compiler checks whether that method exist in variable class name i.e. Shape. If the method is defined (or declared), compiler do not generate an error and accept the call as valid. It do not matter, whether the subclass overrides it.

Definition of calculateArea() exist in both classes i.e. Shape and Rectangle, which class method is actually called? The type of object to which the variable refers, determines the actual method that shall be called. But there are the two possible scenerios:


  1. If child class do not override the method, parent class version would be called.
  2. If child class overrides the method i.e. redefine it, the child class version of the method would be called even variable type is of parent class. So in above case, Rectangle has overriden the calculateArea method, so the method body that is defined in child class i.e. Rectangle, would be called.

You see, the code is same s.calculateArea() ... but whether the method of Shape would be called or the one that is defined in Rectangle class is decided at runtime. So based on the runtime situation, same code may call parent class method (in case the child class do not override it) or it may call the child class method (in case the child class override the method). 'Poly' mean 'many' and 'morphs' means 'forms', so you see, same code has many forms at runtime, depending on how the classes/methods are implemented.

You should understand the basic idea of polymorphism by now. Lets dive in further and try to understand how it help to build extensible systems and reduces the code size. What does it mean by "program in general"? Only then, you would understand what is benefit of overriding and storing child object reference in parent class variable.

How Polymorphims Help to Build Extensible Systems and Reduce Code Size

Lets consider, we need to paint different types of shapes. Our system shall be able to calculate cost to paint each type of shape. That means, we may define a class Painter that shall hold some behavior/method to calculate the painting cost that shall be charged to paint a shape. We assume, the painter charge the cost to area of shape i.e. the wider the area, higher the cost.

First, I share the solution without using polymophism. Here is how our Painter class would look like if we do not use polymorphism to calculate the painting cost:

public class Painter {

    final static int RATE = 50;

    double calculatePaintingCost(Rectangle shape) {
        return shape.calculateArea() * RATE;
    }

    double calculatePaintingCost(Circle shape) {
        return shape.calculateArea() * RATE;
    }

}

We have to define two calculatePaintingCost methods in Painter class because we need to calculate painting cost for two different shape types. If we defined only one method, for example that receives Rectangle object reference. We can't call it passing a reference of Circle object. So if there are 10 different type of shapes, we need to define 10 methods, one for each type.

If you analyze the body of each method, its clearly a waste of effort and bad practice as we are repeating same code in each method. We can improve using polymorphism. How? We know that parent class variable can hold the reference of all child class objects. If we define only one method in Painter class with Shape type parameter, it can be used to calculate painting cost of any type of shape.

Here is the improved code:

public class Painter {

    final static int RATE = 50;

    public double calculatePaintingCost(Shape shape) {
        return shape.calculateArea() * RATE;
    }
}

You see, our code size has reduced (imagine the benefit if there were 10 methods, each for different shape type). How this approach is also extensible? You see, we are processing each shape subclass object generically (or polymorphically), that means, if we add a new shape type in our system in future e.g. Triangle. We would not change the Painter class, as Triangle would also extend the Shape, so calculatePaintingCost(Shape shape) would also process the Triangle type object.

Here is how we would use the Painter class (you see, we are passing different type of shapes to same method and it works):

public class PainterTest {

    public static void main(String[] args) {
        Painter painter = new Painter();

        Rectangle rectangle = new Rectangle(3,4);
        Circle circle = new Circle(5);

        System.out.println(painter.calculatePaintingCost(rectangle));
        System.out.println(painter.calculatePaintingCost(circle));
    }
}

I mentioned, polymorphism means 'program in general' (in the sense of generalization and specialization as discussed in start), you see we got above reduction in code size and extensibility by using the more generic class i.e. Shape rather than using specific class. Object is even more generic than Shape but we have not used Object in calculatePaintingCost method parameter, because we need to call calculateArea() method of passed shape object. If we use Object, we can't call calcualteArea method, becasue that method do not exist in Object. So its important to select appropriate level of generalization and it become clear from context.

Comments