Java Polymorphism

Polymorphism in Java

Polymorphism is one of the core concepts of object-oriented programming (OOP) that allows objects to be treated as instances of their parent class rather than their actual class. The word “polymorphism” means “many shapes” or “many forms.” In Java, polymorphism allows methods to do different things based on the object it is acting upon, even though they share the same name.

Types of Polymorphism

1. Compile-Time Polymorphism (Method Overloading)

Compile-time polymorphism, also known as static polymorphism or method overloading, occurs when multiple methods have the same name but different parameter lists within the same class. The method to be invoked is determined at compile-time.

  • Method Overloading
Java
public class MathUtils {
    // Method to add two integers
    public int add(int a, int b) {
        return a + b;
    }

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

    // Overloaded method to add two double values
    public double add(double a, double b) {
        return a + b;
    }

    public static void main(String[] args) {
        MathUtils math = new MathUtils();
        System.out.println(math.add(2, 3));        // Calls add(int, int)
        System.out.println(math.add(2, 3, 4));     // Calls add(int, int, int)
        System.out.println(math.add(2.5, 3.5));    // Calls add(double, double)
    }
}

2. Runtime Polymorphism (Method Overriding)

Runtime polymorphism, also known as dynamic polymorphism or method overriding, occurs when a subclass provides a specific implementation of a method that is already defined in its superclass. The method to be invoked is determined at runtime based on the object.

  • Method Overriding
Java
class Animal {
    // Superclass method
    void sound() {
        System.out.println("Animal makes a sound");
    }
}

class Dog extends Animal {
    // Overridden method
    @Override
    void sound() {
        System.out.println("Dog barks");
    }
}

class Cat extends Animal {
    // Overridden method
    @Override
    void sound() {
        System.out.println("Cat meows");
    }
}

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

        myDog.sound(); // Calls Dog's sound method
        myCat.sound(); // Calls Cat's sound method
    }
}

Upcasting and Downcasting

Upcasting

Upcasting is the process of treating a subclass object as an instance of its superclass. This is always safe and does not require explicit casting.

  • Example:
Java
  Animal animal = new Dog(); // Upcasting
  animal.sound(); // Calls the overridden sound method in Dog class

Downcasting

Downcasting is the process of treating a superclass object as an instance of its subclass. This requires explicit casting and can be unsafe if not handled properly.

  • Example:
Java
  Animal animal = new Dog(); // Upcasting
  Dog dog = (Dog) animal; // Downcasting
  dog.sound(); // Calls the sound method in Dog class

Polymorphism with Interfaces

Polymorphism is also achieved through interfaces. An interface defines a contract, and any class that implements the interface agrees to implement its methods. This allows objects of different classes to be treated uniformly through the interface type.

  • Example:
Java
  interface Animal {
      void sound();
  }

  class Dog implements Animal {
      @Override
      public void sound() {
          System.out.println("Dog barks");
      }
  }

  class Cat implements Animal {
      @Override
      public void sound() {
          System.out.println("Cat meows");
      }
  }

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

          myDog.sound(); // Calls Dog's sound method
          myCat.sound(); // Calls Cat's sound method
      }
  }

Benefits of Polymorphism

  • Code Reusability: Polymorphism allows for the reuse of existing code by defining common interfaces or base classes.
  • Flexibility and Maintainability: Code becomes more flexible and easier to maintain, as new classes can be introduced with minimal changes to existing code.
  • Reduced Complexity: Polymorphism simplifies code by allowing one interface to be used for a general class of actions.

Example Scenario: Shape Drawing

Consider a scenario where you have different types of shapes (e.g., Circle, Rectangle) and you want to draw them. Polymorphism allows you to define a common interface for all shapes and use it to draw any shape without knowing its specific type at compile-time.

  • Example:
Java
  interface Shape {
      void draw();
  }

  class Circle implements Shape {
      @Override
      public void draw() {
          System.out.println("Drawing a Circle");
      }
  }

  class Rectangle implements Shape {
      @Override
      public void draw() {
          System.out.println("Drawing a Rectangle");
      }
  }

  public class TestShapePolymorphism {
      public static void main(String[] args) {
          Shape shape1 = new Circle();
          Shape shape2 = new Rectangle();

          shape1.draw(); // Calls Circle's draw method
          shape2.draw(); // Calls Rectangle's draw method
      }
  }

By understanding and using polymorphism, you can write more flexible and maintainable code, promoting a cleaner design and enabling easier extension and modification of your applications.

Scroll to Top