Polymorphism

Lets discuss something about Polymorphism.  In short it means many forms/shapes. It simply means using a common name/reference to work with different objects which can be identified with that common name/ reference.

Lets take a evergreen example of Shape. We have different shapes like Square, Circle, Rectangle etc. Now suppose we want to develop application which work with different shape.

So to start with we will develop classes..

class Rectangle {
  public void drawRectangle() {
   System.out.println("I am Rectangle..");
  }
}

class Square {
  public void drawRectangle() {
   System.out.println("I am Square.All have equal length. So fair :-)");
  }
}

Now in you application, you will have write the code as

public class ShapeApplication {
    public static void main(String[] args) {
        String type = args[0];
        if("Rectangle".equals(type)) {
            Rectangle rectangle = new Rectangle();
            rectangle.drawRectangle();
        }
        else if("Square".equals(type)) {
            Square square = new Square();
            square.drawSquare();
        }
    }
}

Now if you run the above application such as: Java ShapeApplication Square, you will get output as: “I am Square.All have equal length. So fair :-)”

So far so good. Now you are asked to handle Circle shape also. You will have to write new class for Circle. But only problem is you will have to modify the existing ShapeApplication class to handle Circle shape. What it means is  every time new type of Shape is introduced, you will have to modify existing working working application. Wouldn’t it be nice if you can handle any shape without modifying your Shape application. Sounds good..

So let’s start the journey to wonderful town called Polymorpshism. I believe this town gets visited by every developer every now and then..

Basically Square, Rectangle, Circle all are different kind of Shapes. Yes, each shape has its own specific behaviour, but still we can say basically these are all shapes &  their behaviour definition can be defined (in Java world we say method) in common super class or interface.  Okay we define interface called Shape which conceptually can represent all shapes.  What we want is that each object should draw its particular Shape. We can generalise method name as draw(). So lets code for polymorphism.

interface Shape {
    public void draw();
}

class Rectangle implements Shape{
    public void draw() {
        System.out.println("I am Rectangle..");
    }
}
class Square implements Shape {
    public void draw() {
        System.out.println("I am Square. I am so fair...all have equal length");
    }
}
class Circle implements Shape {
    public void draw() {
        System.out.println("I am Circle. No rough edges :-)");
    }

Now because we have come up with common class/interface for all types of shape, we can write a factory method which can return appropriate Shape for a specified input String

/**
 * Being a good developer, we will write Javadoc
 * Factory class which returns appropriate Shape object based on user specified string input
 */
public class ShapeFactory {

    public static Shape getShape(String shapeType){

        Shape shape = null;
        if("Rectangle".equals(shapeType)) {
            shape = new Rectangle();
        }
        else if("Square".equals(shapeType)) {
            shape = new Square();
        }
        else if("Circle".equals(shapeType)) {
            shape = new Circle();
        }
        return shape;
    }
}

Now we can rewrite the ShapeApplication as 

public class ShapeApplication {
    public static void main(String[] args) {
        String type = args[0];
        Shape shape = ShapeFactory.getShape(type);
        shape.draw();
    }
}

Now if you run the above application such as: Java ShapeApplication Circle, you will get output as: “I am Circle. No rough edges :-)”.

Now suppose a new Shape needs be handled say Triangle, we will have to just modify the factory method.

/**
 * Factory class which returns appropriate Shape object based on user specified string input
 */
public class ShapeFactory {

    public static Shape getShape(String shapeType){
        Shape shape = null;
        if("Rectangle".equals(shapeType)) {
            shape = new Rectangle();
        }
        else if("Square".equals(shapeType)) {
            shape = new Square();
        }
        else if("Circle".equals(shapeType)) {
            shape = new Circle();
        }
        else if("Triangle".equals(shapeType)) {
            shape = new Triangle();
        }
        return shape;
    }
}

But your existing ShapeApplication class will work with this new Shape type without any changes. In other words, the application (let’s say client application) which works with shape will be able to work with any new additional Shape objects as long as it conforms to Shape interface.

Excellent. Now the main point for which I really wanted to write about. Its here where I find many developers getting confused.

Now suppose a new functionality needs to be added to Circle class for rotating. Being super developer, we are more than happy to write new functionality. No problem. We will add rotate() method the Circle class (& not to other class) as it makes sense to Circle only.

class Circle implements Shape {

    public void draw() {
        System.out.println("I am Circle. No rough edges :-)");
    }
    public void rotate() {
        System.out.println("Its so wonderful to rotate ");
    }

}

Now lets say we write client application as:

Shape shape = new Circle()
shape.draw();
shape.rotate();

What will be the output? Most of the time, I get answers as:
I am Circle. No rough edges 🙂
Its so wonderful to rotate ..

shape.draw() –> yes this works fine. Circle’s draw() method gets called.
shape.rotate(); –> This won’t get compiled.

Even though object is of Circle type & certainly that method is available in that object, but you are using reference of Shape. Shape reference doesn’t have any knowledge about rotate method. It just knows whatever is defined in Shape interface.

Just to reiterate the point, analyse this line:
Shape shape = ShapeFactory.getShape(type);
Here we can get any Shape object. How Java can assume at at compile time that it is going to be Circle object? Even if that line was allowed to compile, question remains what should happen for call to rotate method if you get say Square object?

Each concrete shape class can have its own specific method which doesn’t not make sense for other shapes, we so we won’t be adding that method in Shape interface.  Think from users/client of the Shape functionality. They just the know the contract i.e. behaviour as defined in Shape interface. So you can say they are supposed to work with only those methods as defined in Shape interface. In case client application wants to use specific concrete class, they can cast the reference after checking the type:

if(shape instanceof  Circle) {
    Circle circle = (Circle1)shape;
    circle.rotate();
} 

Or if you say that method make sense for other shapes also, you can add that method to interface & have each concrete class implement it. 

Yes, as per polymorphism, object’s  specific method gets called. i.e  At runtime, which methods gets called depends on actual object.
But which method can be called in the first place depends on reference type. This is a very important concept.