Polymorphism in Java

Polymorphism is one of the fundamental principles of object-oriented programming (OOP) that enables objects to take on multiple forms. It is the ability of an object to represent itself in multiple forms. In Java, there are two types of polymorphism: compile-time (static) polymorphism and runtime (dynamic) polymorphism.

1. Compile-time (Static) Polymorphism:

Compile-time polymorphism, also known as method overloading, occurs when there are multiple methods with the same name in a class, but with different parameter types, order, or number of parameters. During the compilation phase, the compiler determines which method to call based on the arguments passed to the method at the time of calling.

Here's an example of compile-time polymorphism in Java:

                
    public class MyClass {
      public void print(String message) {
        System.out.println(message);
      }

      public void print(int number) {
        System.out.println(number);
      }
    }
                
            

In this example, we have two methods with the same name "print" but different parameter types. The first method takes a String parameter, and the second method takes an integer parameter. Depending on the type of argument passed, the appropriate method will be called.

2. Runtime (Dynamic) Polymorphism:

Runtime polymorphism, also known as method overriding, occurs when a subclass provides its implementation of a method that is already present in its superclass. The method in the subclass must have the same name, return type, and parameters as the method in the superclass.

Here's an example of runtime polymorphism in Java:

                
    class Animal {
      public void makeSound() {
        System.out.println("Animal makes a sound");
      }
    }

    class Cat extends Animal {
      public void makeSound() {
        System.out.println("Meow");
      }
    }

    class Dog extends Animal {
      public void makeSound() {
        System.out.println("Woof");
      }
    }

    public class Main {
      public static void main(String[] args) {
        Animal animal1 = new Cat();
        Animal animal2 = new Dog();

        animal1.makeSound();
        animal2.makeSound();
      }
    }
                
            

In this example, we have an Animal class and two subclasses, Cat and Dog. The makeSound() method is overridden in both subclasses with their specific implementations. During runtime, the makeSound() method of the subclass is called based on the type of the object the reference variable is pointing to. In this case, animal1 is pointing to an object of type Cat, so the makeSound() method of the Cat class is called, and animal2 is pointing to an object of type Dog, so the makeSound() method of the Dog class is called.