Understanding Polymorphism in Java
Polymorphism in Java allows a single entity, such as a method, to take multiple forms. This is a key aspect of object-oriented programming, enhancing code flexibility and reusability. The two primary forms of polymorphism in Java are method overloading and method overriding.
Method Overloading (Compile-Time Polymorphism)
Method overloading occurs when two or more methods in the same class have the same name but different parameters (in number, type, or both). This allows a method to perform different functions based on the input parameters. It is resolved at compile-time, hence known as compile-time polymorphism.
Example of Method Overloading:
public class Calculator {
// 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;
}
}
public class Main {
public static void main(String[] args) {
Calculator calc = new Calculator();
System.out.println(calc.add(10, 20)); // Calls first add method
System.out.println(calc.add(10, 20, 30)); // Calls overloaded add method
}
}
In this example, the Calculator
class has two versions of the add
method: one that adds two integers and another that adds three integers. The compiler determines which method to call based on the number of arguments.
Method Overriding (Runtime Polymorphism)
Method overriding occurs when a subclass provides a specific implementation for a method that is already defined in its superclass. This allows a subclass to define a behavior specific to it, which is different from the superclass. The method to be executed is determined at runtime based on the object type, which is why it’s called runtime polymorphism.
Example of Method Overriding:
public class Animal {
public void sound() {
System.out.println("Animal makes a sound");
}
}
public class Dog extends Animal {
// Overriding the sound method
@Override
public void sound() {
System.out.println("Dog barks");
}
}
public class Main {
public static void main(String[] args) {
Animal myAnimal = new Dog();
myAnimal.sound(); // Outputs "Dog barks"
}
}
In this example, the sound
method in the Dog
class overrides the sound
method defined in the Animal
superclass. When an object of type Dog
is treated as an Animal
and the sound()
method is invoked, the overridden method in Dog
is executed, displaying “Dog barks”.
Polymorphism and Inheritance at Work in Java
Example: Animals and Their Sounds
Consider a simple example with a superclass Animal
and its subclasses Dog
and Cat
. Each subclass will override a method from Animal
to exhibit polymorphic behavior.
// Superclass
public class Animal {
public void makeSound() {
System.out.println("Animal makes a sound");
}
}
// Subclass 1
public class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("Dog barks");
}
}
// Subclass 2
public class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("Cat meows");
}
}
// Main class to demonstrate polymorphism
public class Main {
public static void main(String[] args) {
Animal myAnimal;
// Dog instance treated as an Animal
myAnimal = new Dog();
myAnimal.makeSound(); // Outputs "Dog barks"
// Cat instance treated as an Animal
myAnimal = new Cat();
myAnimal.makeSound(); // Outputs "Cat meows"
}
}
Explanation
- The
Animal
class has a methodmakeSound()
. - The
Dog
andCat
classes extendAnimal
and override themakeSound()
method. - In the
Main
class, we declare a variablemyAnimal
of typeAnimal
. We then instantiateDog
andCat
objects, but treat them asAnimal
objects. This is where polymorphism is at play. - When we call
myAnimal.makeSound()
, the version of the method that gets executed depends on the actual object type (Dog
orCat
), not the reference type (Animal
).