Introduction
Polymorphism is one of the four fundamental principles of object-oriented programming, along with encapsulation, inheritance, and abstraction.
The word polymorphism means “many forms.” In Java, it allows the same method name or parent-class reference to represent different operations and behaviors.
For example, a method named makeSound() may produce different results for a dog, cat, and bird.
Polymorphism
Java supports two primary types of polymorphism:
Compile-time polymorphism
Method overloading
Constructor overloading
Runtime polymorphism
Method overriding
Why Is Polymorphism Important?
Polymorphism helps developers:
Reuse existing code
Avoid duplicate logic
Create flexible class structures
Provide different implementations of a common operation
Extend applications without changing large amounts of code
Reduce dependencies between classes
Improve code readability and maintainability
Compile-Time Polymorphism
Compile-time polymorphism occurs when the compiler determines which method or constructor should be called.
The decision is made according to the arguments supplied in the method or constructor call.
Compile-time polymorphism is also known as:
Static polymorphism
Early binding
Static binding
Java implements compile-time polymorphism through:
Method overloading
Constructor overloading
Method Overloading
Method overloading means declaring multiple methods with the same name but different parameter lists.
Overloaded methods may differ in:
Number of parameters
Types of parameters
Order of parameter types
Method Overloading Example
// Calculator class containing overloaded methodsclass Calculator { // Adds two integer values int add(int first, int second) { return first + second; } // Adds two decimal values double add(double first, double second) { return first + second; } // Adds three integer values int add(int first, int second, int third) { return first + second + third; }}// Main class used to execute the programpublic class Main { public static void main(String[] args) { // Create an object of the Calculator class Calculator calculator = new Calculator(); // Calls add(int, int) int integerResult = calculator.add(10, 5); // Calls add(double, double) double decimalResult = calculator.add(4.5, 2.5); // Calls add(int, int, int) int threeNumberResult = calculator.add(2, 3, 4); // Display the results System.out.println(integerResult); // Output : 15 System.out.println(decimalResult); // Output : 7.0 System.out.println(threeNumberResult); // Output : 9 }}All three methods are named add(), but their parameter lists are different. The compiler examines the arguments and selects the appropriate method.
Method Overloading
Constructor Overloading
Constructor overloading means declaring multiple constructors in the same class with different parameter lists.
It allows objects of the same class to be initialized in different ways.
Constructors are not methods, but constructor overloading is considered compile-time polymorphism because the compiler selects the appropriate constructor according to the supplied arguments.
Constructor Overloading Example
// Student class with overloaded constructorsclass Student { // Private attributes of the Student class private String name; private int age; // No-argument constructor Student() { // Assign default values name = "Unknown"; age = 0; } // Constructor accepting only the student's name Student(String name) { // Store the supplied name this.name = name; // Assign a default age age = 0; } // Constructor accepting both name and age Student(String name, int age) { // Store the supplied values this.name = name; this.age = age; } // Display the student's information void display() { System.out.println(name + " - " + age); }}// Main class used to execute the programpublic class Main { public static void main(String[] args) { // Calls the no-argument constructor Student firstStudent = new Student(); // Calls the constructor accepting a name Student secondStudent = new Student("Aman"); // Calls the constructor accepting a name and age Student thirdStudent = new Student("Riya", 20); // Display the three student objects firstStudent.display(); // Output : Unknown - 0 secondStudent.display(); // Output : Aman - 0 thirdStudent.display(); // Output : Riya - 20 }}The compiler selects one of these constructors based on the arguments used while creating the object.
Constructor Overloading
Runtime Polymorphism
Runtime polymorphism in Java is implemented through method overriding.
It occurs when a child class provides its own implementation of a method inherited from a parent class.
The method that executes is determined by the actual object type at runtime.
Runtime polymorphism is also known as:
Dynamic polymorphism
Late binding
Dynamic binding
Dynamic method dispatch
Method Overriding
Method overriding occurs when:
A parent class declares an instance method.
A child class inherits that method.
The child class provides a new implementation with the same signature.
Method Overriding Example
// Parent class representing a general animalclass Animal { // Method that may be overridden by child classes void sound() { System.out.println("The animal makes a sound."); }}// Dog class inherits from Animalclass Dog extends Animal { // Override the sound() method inherited from Animal @Override void sound() { System.out.println("The dog barks."); }}// Cat class inherits from Animalclass Cat extends Animal { // Override the sound() method inherited from Animal @Override void sound() { System.out.println("The cat meows."); }}// Main class used to execute the programpublic class Main { public static void main(String[] args) { /* * Parent reference containing a Dog object. * This is called upcasting. */ Animal firstAnimal = new Dog(); /* * Parent reference containing a Cat object. * This is also an example of upcasting. */ Animal secondAnimal = new Cat(); /* * The object is a Dog, so Dog's sound() method runs. */ firstAnimal.sound(); // Output : The dog barks. /* * The object is a Cat, so Cat's sound() method runs. */ secondAnimal.sound(); // Output : The cat meows. }}Although both variables have the reference type Animal, they contain different child objects.
Animal firstAnimal = new Dog();
Animal secondAnimal = new Cat();Java checks the actual object type at runtime and calls the corresponding overridden method.
Method Overriding
Rules for Method Overriding
When overriding a method:
A parent-child relationship must exist.
The method name must remain the same.
The parameter list must remain the same.
The return type must be the same or covariant.
The child method cannot use a more restrictive access modifier.
A
finalmethod cannot be overridden.A
privatemethod cannot be overridden because it is not inherited.Static methods are hidden, not overridden.
Constructors cannot be overridden.
The
@Overrideannotation should be used.
Difference between Method Overloading and Method Overriding
Feature | Method Overloading | Method Overriding |
|---|---|---|
Polymorphism type | Compile-time | Runtime |
Relationship required | Usually within one class | Parent-child relationship |
Method name | Same | Same |
Parameters | Must be different | Must be compatible with the inherited method |
Return type | May differ when parameter lists differ | Same or covariant |
Selection occurs | During compilation | During execution |
Main purpose | Provide multiple ways to perform an operation | Provide specialized child behavior |
Summary
Polymorphism means “many forms” and allows the same method name or parent-class reference to represent different behaviours. Java supports compile-time polymorphism through method and constructor overloading, where the compiler selects the appropriate version based on the arguments.
Runtime polymorphism is achieved through method overriding, where a child class provides its own implementation of a parent method. The actual object type determines which overridden method runs, making code more flexible, reusable, and maintainable.
Be the first to add a comment.