Polymorphism
Difference between Inheritance and Polymorphism
Inheritance:
Inheritance is a feature in object-oriented programming where a new class (called derived or child class) is created by inheriting properties and behaviors (methods and variables) from an existing class (called base or parent class). It promotes code reusability and reduces redundancy.
Types of Inheritance:
1. Single Inheritance
2. Multi-level Inheritance
3. Multiple Inheritance
4. Hybrid Inheritance
5. Hierarchical Inheritance
Example of Inheritance:
#include <iostream>
using namespace std;
class A {
int a, b;
public:
void add(int x, int y)
{
a = x;
b = y;
cout << "Addition of a + b is: " << (a + b) << endl;
}
};
class B : public A {
public:
void print(int x, int y)
{
add(x, y);
}
};
int main()
{
B b1;
b1.print(5, 6);
return 0;
}
Output:
Addition of a + b is: 11
Here, class B inherits the add()
method from class A.
Polymorphism:
Polymorphism is a feature in object-oriented programming where an object can take multiple forms. Polymorphism allows one task to be performed in different ways, either at compile-time or run-time.
Types of Polymorphism:
- Compile-time polymorphism (Method Overloading)
- Run-time polymorphism (Method Overriding)
Example of Polymorphism:
#include <iostream>
using namespace std;
class A {
int a, b, c;
public:
// Compile-time polymorphism (Method Overloading)
void add(int x, int y)
{
a = x;
b = y;
cout << "Addition of a + b is: " << (a + b) << endl;
}
void add(int x, int y, int z)
{
a = x;
b = y;
c = z;
cout << "Addition of a + b + c is: " << (a + b + c) << endl;
}
// Run-time polymorphism (Method Overriding)
virtual void print()
{
cout << "Class A's method is running" << endl;
}
};
class B : public A {
public:
void print()
{
cout << "Class B's method is running" << endl;
}
};
int main()
{
A a1;
// Compile-time polymorphism (Method Overloading)
a1.add(6, 5);
a1.add(1, 2, 3);
B b1;
// Run-time polymorphism (Method Overriding)
b1.print();
}
Output:
Addition of a + b is: 11
Addition of a + b + c is: 6
Class B's method is running
Difference between Inheritance and Polymorphism:
Inheritance | Polymorphism | |
---|---|---|
Inheritance allows a new class (derived class) to inherit features from an existing class (base class). | Polymorphism allows methods to take multiple forms. | |
Inheritance applies to classes. | Polymorphism applies to methods or functions. | |
Inheritance supports code reusability and reduces code duplication. | Polymorphism allows the program to choose which function to execute at compile-time (overloading) or run-time (overriding). | |
Inheritance can be single, multiple, hierarchical, multilevel, or hybrid. | Polymorphism can be compile-time (overloading) or run-time (overriding). | |
Inheritance is used to model relationships between classes. | Polymorphism allows flexibility in implementing methods. |
Example of Inheritance:
A class Car
can be derived from a class Vehicle
, and Car
can further inherit properties like engine type, wheels, etc.
Example of Polymorphism:
The class Car
can have a method setColor()
, which changes the car’s color based on the input color value provided.
Function Overriding in C++
Method Overriding and Runtime Polymorphism in Java:
Java supports runtime polymorphism through method overriding. Dynamic method dispatch is the mechanism that resolves which overridden method will be executed at runtime, not during compile-time.
When a method is called on a superclass reference, Java determines which version of the method (from the superclass or subclass) to execute based on the actual object being referenced at the time of the call. This decision is made at runtime, depending on the type of the object (not the reference variable).
A superclass reference variable can refer to an object of a subclass, which is known as upcasting. Java uses this concept to enable method overriding during runtime.
If a superclass contains a method that is overridden by a subclass, the version of the method executed depends on the object type being referred to, even though the reference variable is of the superclass type. Below is an example demonstrating dynamic method dispatch:
Java Example of Dynamic Method Dispatch:
// A Java program to demonstrate Dynamic Method Dispatch
class Animal {
void sound() {
System.out.println("Animal makes a sound");
}
}
class Dog extends Animal {
// overriding sound() method
void sound() {
System.out.println("Dog barks");
}
}
class Cat extends Animal {
// overriding sound() method
void sound() {
System.out.println("Cat meows");
}
}
public class DispatchDemo {
public static void main(String[] args) {
// creating objects of Animal, Dog, and Cat
Animal animal = new Animal();
Dog dog = new Dog();
Cat cat = new Cat();
// reference of type Animal
Animal ref;
// ref refers to Animal object
ref = animal;
ref.sound(); // calls Animal's version of sound()
// ref refers to Dog object
ref = dog;
ref.sound(); // calls Dog's version of sound()
// ref refers to Cat object
ref = cat;
ref.sound(); // calls Cat's version of sound()
}
}
Output:
Animal makes a sound
Dog barks
Cat meows
Explanation:
The above program has one superclass Animal
and two subclasses Dog
and Cat
. Each subclass overrides the sound()
method from the superclass.
- First, an object of each class (
Animal
,Dog
,Cat
) is created. - Then a reference of type
Animal
is used to refer to objects of different types (upcasting). - The version of the
sound()
method called depends on the actual object type at runtime.
Example: Runtime Polymorphism with Data Members (Java):
In Java, runtime polymorphism works with methods but not with data members (variables). Variables are not overridden, so the reference variable will always access the data member of the superclass, not the subclass.
Java Example for Data Members:
// Java program to show that runtime polymorphism
// doesn't apply to data members (only methods)
class Animal {
int age = 5;
}
class Dog extends Animal {
int age = 10;
}
public class TestDemo {
public static void main(String[] args) {
Animal animal = new Dog(); // object of type Dog
// Data member of class Animal will be accessed
System.out.println(animal.age);
}
}
Output:
5
Explanation:
In this program, both the Animal
and Dog
classes have a common data member age
. Even though the object is of type Dog
and the reference variable is of type Animal
, the data member of Animal
will be accessed because data members are not overridden. Therefore, animal.age
refers to the superclass Animal
‘s age
value.
Advantages of Dynamic Method Dispatch:
1. Supports Method Overriding:
Dynamic method dispatch allows Java to support method overriding, which is crucial for implementing runtime polymorphism.
2. Provides Flexibility in Method Implementation:
A class can define common methods that are shared by all its subclasses, while allowing each subclass to provide specific implementations for those methods.
3. Enhances Extensibility:
It enables subclasses to add their unique behaviors while still using the reference variable of the superclass, promoting flexibility and scalability in code.
Difference between Compile-time and Run-time Polymorphism in Java
Polymorphism Explained:
The term polymorphism refers to the concept of having multiple forms. In simpler terms, polymorphism allows the same message or method to be processed in more than one way. In this discussion, we will explore the distinction between the two types of polymorphism: compile-time and runtime polymorphism.
Compile-Time Polymorphism:
When the binding of a method call to the method definition occurs at compile-time, it is known as compile-time polymorphism. Java resolves which method to call by examining the method signatures during compilation, which is why this type of polymorphism is also called static or early binding. Compile-time polymorphism is achieved through method overloading.
Method Overloading refers to having multiple methods in the same class with the same name but different parameter lists (method signatures). This is one way to implement polymorphism, although the specific method varies based on the language. In Java, method overloading is resolved at compile-time.
Here is an example demonstrating compile-time polymorphism:
Java Example of Compile-Time Polymorphism:
// Java program demonstrating compile-time polymorphism
public class Example {
// First version of the add method
public static int add(int a, int b) {
return a + b;
}
// Second version of the add method
public static double add(double a, double b) {
return a + b;
}
public static void main(String[] args) {
// The first add method is called
System.out.println(add(3, 4)); // Output: 7
// The second add method is called
System.out.println(add(3.5, 4.5)); // Output: 8.0
}
}
Output:
7
8.0
Run-Time Polymorphism:
When the method binding happens at runtime, it is called runtime polymorphism. This is achieved through method overriding in Java. The Java Virtual Machine (JVM) determines which overridden method to call at runtime based on the actual object, not during compilation.
Method Overriding occurs when a subclass provides its specific implementation of a method that is already defined in its superclass. This allows different classes to define the same method in their own way, which is resolved dynamically at runtime.
Java Example of Run-Time Polymorphism:
// Java program demonstrating run-time polymorphism
// Parent class
class Animal {
public void makeSound() {
System.out.println("Animal makes a sound");
}
}
// Child class
class Dog extends Animal {
// Overriding the parent method
public void makeSound() {
System.out.println("Dog barks");
}
}
public class Main {
public static void main(String[] args) {
Animal animalRef = new Dog(); // Upcasting
// The overridden method in Dog will be called
animalRef.makeSound(); // Output: Dog barks
}
}
Output:
Dog barks
In this example, even though the reference variable animalRef
is of type Animal
, it refers to an object of type Dog
. At runtime, the JVM determines which makeSound
method to execute based on the object type, resulting in the Dog
class’s method being called.
Differences between Compile-Time and Run-Time Polymorphism:
Compile-Time Polymorphism | Run-Time Polymorphism |
---|---|
The method call is resolved by the compiler. | The method call is resolved during runtime by the JVM. |
Also known as Static binding, Early binding, or Overloading. | Also known as Dynamic binding, Late binding, or Overriding. |
Achieved by method overloading. | Achieved by method overriding. |
Faster execution, as the method to execute is determined at compile time. | Slower execution in comparison, as the method is determined at runtime. |
Does not involve inheritance. | Requires inheritance for method overriding. |
Less flexible, since method calls are fixed at compile-time. | More flexible, since method calls are resolved at runtime. |
Advantages of Polymorphism:
- Compile-Time Polymorphism: Provides faster execution as the decision of which method to invoke is made at compile time, making the program more efficient.
- Run-Time Polymorphism: Offers greater flexibility because method calls are determined at runtime, allowing different behaviors based on the actual object type being referenced.