Java Polymorphism Example: A Comprehensive Guide

Java Polymorphism Example
Rate this post

Polymorphism is one of the core concepts of Object-Oriented Programming (OOP), and Java is no exception in utilizing this powerful feature. In simple terms, polymorphism allows objects of different classes to be treated as objects of a common superclass. The primary benefit of polymorphism is that it enables one interface to be used for a general class of actions, making your code more flexible and reusable.

In this article, we’ll explore a Java polymorphism example that demonstrates how polymorphism works in Java.

Types of Polymorphism in Java

Polymorphism in Java can be understood in two main types:

  1. Compile-Time Polymorphism (Method Overloading): This type of polymorphism occurs when multiple methods have the same name but differ in parameters (number or type of parameters).
  2. Runtime Polymorphism (Method Overriding): This type of polymorphism occurs when a subclass redefines a method of its superclass, allowing the method to behave differently based on the object type.

Both types of polymorphism help achieve more flexible and maintainable code.

Java Polymorphism Example: Method Overloading (Compile-Time Polymorphism)

Let’s first look at method overloading to understand how compile-time polymorphism works. Here’s an example:

javaCopyclass Calculator {

    // Overloaded method for adding two integers
    public int add(int a, int b) {
        return a + b;
    }

    // Overloaded method for adding three integers
    public int add(int a, int b, int c) {
        return a + b + c;
    }
}

public class Main {
    public static void main(String[] args) {
        Calculator calculator = new Calculator();

        // Calling the method with two parameters
        System.out.println("Sum of two numbers: " + calculator.add(5, 10));

        // Calling the method with three parameters
        System.out.println("Sum of three numbers: " + calculator.add(5, 10, 15));
    }
}

In this example, the method add is overloaded with two different signatures. The Java compiler determines which method to call based on the number of arguments passed, demonstrating compile-time polymorphism.

Java Polymorphism Example: Method Overriding (Runtime Polymorphism)

Next, let’s look at method overriding, which is an example of runtime polymorphism. In this case, a subclass provides its own implementation of a method already defined in its superclass.

javaCopyclass Animal {
    // Method in superclass
    public void sound() {
        System.out.println("Animal makes a sound");
    }
}

class Dog extends Animal {
    // Method overriding in subclass
    @Override
    public void sound() {
        System.out.println("Dog barks");
    }
}

class Cat extends Animal {
    // Method overriding in subclass
    @Override
    public void sound() {
        System.out.println("Cat meows");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal myAnimal = new Animal();
        Animal myDog = new Dog();
        Animal myCat = new Cat();

        myAnimal.sound(); // Output: Animal makes a sound
        myDog.sound();    // Output: Dog barks
        myCat.sound();    // Output: Cat meows
    }
}

In this example, the sound method is defined in the superclass Animal. However, both Dog and Cat classes override this method. When we call sound on an Animal reference that points to a Dog or Cat object, Java determines the method to execute at runtime, which is a clear example of runtime polymorphism.

Benefits of Polymorphism in Java

  1. Flexibility: Polymorphism allows you to write more flexible code. For instance, you can create a collection of objects that share the same superclass type but have different behaviors.
  2. Code Reusability: By using polymorphism, you can reuse methods in different contexts without changing the original implementation.
  3. Maintainability: Polymorphism helps in reducing code duplication and allows easier modifications.

Real-Life Example of Java Polymorphism

To understand Java Polymorphism more clearly, let’s look at some real-life examples of both Compile-Time Polymorphism (Method Overloading) and Runtime Polymorphism (Method Overriding).

1. Compile-Time Polymorphism (Method Overloading)

Real-Life Scenario: Imagine you are booking a hotel room. The method to book the room could be used in different scenarios based on how many people are going to stay in the room.

  • Overloaded Method Example:
    • One version of the bookRoom method takes one parameter (the number of people).
    • Another version of the method takes two parameters: one for the number of people and another for additional requests (such as a bed type or special accommodations).

This is a real-life scenario where you need multiple ways to call the same method depending on the situation.

javaCopyclass Hotel {

    // Method for booking a room for a single person
    public void bookRoom(int people) {
        System.out.println("Booking a room for " + people + " person.");
    }

    // Overloaded method for booking a room with special requests
    public void bookRoom(int people, String specialRequest) {
        System.out.println("Booking a room for " + people + " person with special request: " + specialRequest);
    }
}

public class Main {
    public static void main(String[] args) {
        Hotel hotel = new Hotel();
        
        // Calling the method for one person
        hotel.bookRoom(1);

        // Calling the overloaded method for two people with a special request
        hotel.bookRoom(2, "King size bed");
    }
}

Explanation:

  • The bookRoom method is overloaded with two different signatures. One accepts a single integer to book for a single person, and the other accepts an integer for the number of people and a string for special requests.
  • Compile-time polymorphism determines which method to call based on the number of arguments during the compilation.

2. Runtime Polymorphism (Method Overriding)

Real-Life Scenario: Imagine you are designing a vehicle rental system. The Vehicle class could have a start() method, but different types of vehicles (like Car, Bike, Truck) may start in different ways.

  • The Vehicle class has a general start() method.
  • The Car, Bike, and Truck subclasses each override the start() method to provide their own specific implementation.

This is a real-life scenario where different objects (vehicles) start in different ways, but we treat them generically as a Vehicle in the system.

javaCopyclass Vehicle {
    // Method in superclass
    public void start() {
        System.out.println("Starting the vehicle.");
    }
}

class Car extends Vehicle {
    // Overridden method in Car subclass
    @Override
    public void start() {
        System.out.println("Starting the car with key ignition.");
    }
}

class Bike extends Vehicle {
    // Overridden method in Bike subclass
    @Override
    public void start() {
        System.out.println("Starting the bike with a kick.");
    }
}

class Truck extends Vehicle {
    // Overridden method in Truck subclass
    @Override
    public void start() {
        System.out.println("Starting the truck with a button.");
    }
}

public class Main {
    public static void main(String[] args) {
        Vehicle vehicle1 = new Car();
        Vehicle vehicle2 = new Bike();
        Vehicle vehicle3 = new Truck();
        
        // Calling the overridden start() method based on the actual object type
        vehicle1.start();  // Output: Starting the car with key ignition.
        vehicle2.start();  // Output: Starting the bike with a kick.
        vehicle3.start();  // Output: Starting the truck with a button.
    }
}

Explanation:

  • In this example, we have method overriding where each subclass (Car, Bike, Truck) provides its own implementation of the start() method.
  • Runtime polymorphism happens when the program decides which start() method to call based on the actual object type, not the reference type (Vehicle).
    • If a Vehicle reference points to a Car, the start() method in the Car class is called.
    • This decision happens at runtime, making it a runtime polymorphism example.

FAQs

1. What is the difference between method overloading and method overriding in Java?

  • Method Overloading occurs at compile-time, where a method with the same name is defined but with different parameters (either number or type).
  • Method Overriding occurs at runtime, where a subclass provides a specific implementation of a method that is already defined in its superclass.

2. Can we achieve polymorphism without inheritance in Java?

  • No, polymorphism in Java is closely tied to inheritance. In order to use polymorphism, you need a superclass and one or more subclasses, where the method in the superclass is overridden by the subclasses.

3. How do you determine which method is called during runtime in method overriding?

  • The method that gets called at runtime is determined by the type of the object, not the reference variable type. This is why it is called runtime polymorphism. For example, if a reference of type Animal points to a Dog object, the Dog‘s overridden method is called.

4. Is polymorphism possible in static methods?

  • No, polymorphism does not apply to static methods because static methods are resolved at compile time based on the reference type, not the object type. Static methods cannot be overridden; they are hidden in the subclass.

5. Can polymorphism improve the performance of Java applications?

  • While polymorphism is essential for flexibility and code maintainability, it doesn’t directly impact performance. However, its use can lead to better-structured and more optimized code in the long run, as it promotes reusability and scalability.

6. Can we override a private or static method in Java?

  • No, private methods cannot be overridden because they are not visible to subclasses. Similarly, static methods cannot be overridden; they can be hidden by redefining them in the subclass, but they are not polymorphic in nature.

7. What is dynamic method dispatch in Java?

  • Dynamic method dispatch refers to the process by which Java decides at runtime which version of an overridden method to call. This decision is based on the actual object type, not the reference variable type, and is a key feature of runtime polymorphism.

8. Can we achieve polymorphism using interfaces in Java?

  • Yes, polymorphism can be achieved using interfaces. Classes that implement the same interface can provide different implementations of the methods declared in the interface, allowing polymorphic behavior.

9. What is the role of super in method overriding?

  • The super keyword is used to call methods from the superclass. In the case of method overriding, you can use super to call the overridden method in the superclass from the subclass, especially if you need to extend the functionality of the superclass method.

10. What happens if a subclass does not override a method in Java?

  • If a subclass does not override a method from its superclass, the subclass inherits the method from the superclass. The superclass’s method will be invoked when the method is called on an object of the subclass.

11. Can polymorphism be used with constructors in Java?

  • No, polymorphism cannot be applied to constructors because constructors are not inherited by subclasses. While a subclass can have its own constructor, it cannot override the constructor of its superclass.

12. Can polymorphism be used with final methods in Java?

  • No, polymorphism cannot be applied to final methods. The final keyword prevents a method from being overridden in a subclass, meaning the method is resolved at compile time and is not subject to polymorphism.

Learn about Creating Objects with “new” keyword vs. Using Factory Methods in Java