Java Polymorphism: Unleashing the Full Potential of Object-Oriented Programming

Kayis Rahman
4 min readMay 1, 2023

--

Polymorphism is the ability of objects of different classes to be used interchangeably. This is made possible by inheritance, where a subclass can be treated as its parent class. Polymorphism allows us to write code that works with objects of different classes, as long as they share a common ancestor class or interface.

In Java, polymorphism is most commonly achieved through method overriding. When a subclass overrides a method of its parent class, it can provide its own implementation of the method. When an object of the subclass is referred to using a reference of its parent class, the implementation of the method that is called is the one in the subclass.

For example, consider the following code:

public class Animal {
public void speak() {
System.out.println("I am an animal.");
}
}

public class Dog extends Animal {
@Override
public void speak() {
System.out.println("Woof!");
}
}

public class Cat extends Animal {
@Override
public void speak() {
System.out.println("Meow!");
}
}

public class Main {
public static void main(String[] args) {
Animal a1 = new Animal();
Animal a2 = new Dog();
Animal a3 = new Cat();

a1.speak(); // "I am an animal."
a2.speak(); // "Woof!"
a3.speak(); // "Meow!"
}
}

Here, we define an Animal class with a speak method that prints "I am an animal," and two subclasses, Dog and Cat, that override the speak method with their own implementations. In the main method, we create objects of each class and assign them to variables of type Animal. When we call the speak method on each object, Java determines which implementation of the method to call based on the actual type of the object (Animal, Dog, or Cat), not the reference type (Animal). This allows us to write code that works with objects of different classes, as long as they share a common ancestor class.

Another way to achieve polymorphism in Java is through interfaces. An interface is a collection of abstract methods that define a contract for classes that implement the interface. When a class implements an interface, it must provide implementations for all of the methods defined in the interface. This allows us to write code that works with objects of different classes, as long as they implement the same interface.

For example, consider the following code:

public interface Shape {
double getArea();
}

public class Rectangle implements Shape {
private double width;
private double height;

public Rectangle(double width, double height) {
this.width = width;
this.height = height;
}

@Override
public double getArea() {
return width * height;
}
}

public class Circle implements Shape {
private double radius;

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

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

public class Main {
public static void main(String[] args) {
Shape s1 = new Rectangle(3, 4);
Shape s2 = new Circle(2);

System.out.println(s1.getArea()); // 12.0
System.out.println(s2.getArea()); // 12.566370614359172
}
}

Here, we define an interface Shape with an abstract method getArea, which is implemented by two classes, Rectangle and Circle. In the main method, we create objects of Rectangle and Circle and assign them to variables of type Shape. When we call the getArea method on each object, Java determines which implementation of the method to call based on the actual type of the object (Rectangle or Circle), not the reference type (Shape). This allows us to write code that works with objects of different classes, as long as they implement the same interface.

Polymorphism is a powerful feature of object-oriented programming that allows us to write flexible and reusable code. By designing classes and interfaces that share common behavior, we can write code that works with objects of different classes and interfaces, without having to write separate code for each class or interface.

Here’s a Java code example that demonstrates polymorphism using the Shape interface and the Rectangle and Circle classes:

public interface Shape {
public double getArea();
}

public class Rectangle implements Shape {
private double width;
private double height;

public Rectangle(double width, double height) {
this.width = width;
this.height = height;
}

@Override
public double getArea() {
return width * height;
}
}

public class Circle implements Shape {
private double radius;

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

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

public class Main {
public static void main(String[] args) {
Shape rect = new Rectangle(5, 10);
Shape circle = new Circle(5);

System.out.println(rect.getArea()); // Output: 50.0
System.out.println(circle.getArea()); // Output: 78.53981633974483
}
}

In this example, we define the Shape interface with a single method, getArea(). We then implement this interface in the Rectangle and Circle classes, providing their own implementation of the getArea() method. Finally, in the Main class, we create objects of the Rectangle and Circle classes and assign them to variables of type Shape. We can then call the getArea() method on these objects, and Java will automatically determine which implementation of the method to call based on the actual type of the object (Rectangle or Circle). This demonstrates the power of polymorphism in allowing us to write flexible and reusable code.

--

--