Polymorphism Types In OOPs
In programming languages and type theory, polymorphism is the provision of a single interface to entities of different types, or the use of a single symbol to represent multiple different types. A polymorphic object is an object that is capable of taking on multiple forms, just like your ex– just kidding!
Compile-time Polymorphism (Static Binding or Early Binding or Ad hoc polymorphism):
There are two types of compile time polymorphism exist one is operator overloading and other is method overloading
Method Overloading: Allows a class to have multiple methods with the same name but different parameters. Here only difference in parameter only not return type of method.
using System;
public class Calculator
{
public int Add(int a, int b)
{
return a + b;
}
// Overloaded method with different parameter types
public double Add(double a, double b)
{
return a + b;
}
// this is not possible can't have same paramter but different return type
public int Add(double a, double b)
{
return (int) a + b;
}
}
class Program
{
static void Main(string[] args)
{
Calculator calc = new Calculator();
int result1 = calc.Add(10, 20);
double result2 = calc.Add(3.5, 4.5);
Console.WriteLine("Result 1: " + result1);
Console.WriteLine("Result 2: " + result2);
}
}
Operator Overloading: Allows operators to be redefined to work with user-defined types. Like you can add number in string type.
using System;
public class Complex
{
public int Real { get; set; }
public int Imaginary { get; set; }
public Complex(int real = 0, int imaginary = 0)
{
Real = real;
Imaginary = imaginary;
}
// Overloading the '+' operator for adding two Complex objects
public static Complex operator +(Complex c1, Complex c2)
{
return new Complex(c1.Real + c2.Real, c1.Imaginary + c2.Imaginary);
}
// Overloading the '!=' operator for adding two Complex objects
public static bool operator !=(Complex first, Complex second)
{
return !(first.Real == second.Real && first.Imaginary == second.Imaginary);
}
public void Display()
{
Console.WriteLine($"Real: {Real}, Imaginary: {Imaginary}");
}
}
class Program
{
static void Main(string[] args)
{
Complex c1 = new Complex(3, 4);
Complex c2 = new Complex(5, 6);
Complex result = c1 + c2; // Using the overloaded '+' operator
result.Display();
}
}
in the above example complex class overloaded + and != operator, using operator overloading you are providing custom natural operator overloading either -, +, * or = etc. All things you define in compile time that’s why this called compile time overloading. By default in c# there is default + operator overloading between integer and string.
Run-time Polymorphism (Dynamic Binding or Late Binding):
Method Overriding: Occurs when a subclass provides a specific implementation of a method that is already defined in its superclass or parent class . The subclass or child class method overrides the superclass method with the same signature and same return type.
using System;
// Base class
class Shape
{
public virtual void Draw()
{
Console.WriteLine("Drawing a shape");
}
}
// Derived class
class Circle : Shape
{
public override void Draw()
{
Console.WriteLine("Drawing a circle");
}
}
class Program
{
static void Main(string[] args)
{
// Runtime binding example
Shape shape = new Circle(); // Creating instance of derived class but of base class reference
shape.Draw(); // This will call the overridden method in Circle class at runtime
}
}
Method Overriding:
- In this example, the
Shape
class defines a virtual methodDraw()
. - The
Circle
class inherits fromShape
and overrides theDraw()
method with its own implementation. - Method overriding occurs when a subclass provides a specific implementation of a method that is already defined in its superclass (
Shape
). - The
Draw()
method inCircle
overrides theDraw()
method inShape
with the same signature and return type.
Runtime Binding:
- In the
Main
method, an instance ofCircle
is created and assigned to a reference of typeShape
. - This is an example of polymorphism (subtype polymorphism) Later Section i will explain this, where a derived class (
Circle
) object can be treated as its base class (Shape
) type. - At runtime, when
shape.Draw()
is called, the C# runtime system determines the actual type of the object (Circle
) and executes the overridden method inCircle
. - This is known as runtime binding or late binding, where the decision of which method to call is made during runtime based on the actual type of the object.
Parametric Polymorphism (Generic Programming):
Generics: Allows writing functions or classes that can operate on a wide range of data types without knowing the specific type at compile time. It provides flexibility and code reusability by abstracting over types.
using System;
public class Stack<T>
{
private T[] elements;
private int top;
public Stack(int size)
{
elements = new T[size];
top = -1;
}
public void Push(T item)
{
if (top == elements.Length - 1)
{
Console.WriteLine("Stack overflow!");
return;
}
elements[++top] = item;
}
public T Pop()
{
if (top == -1)
{
Console.WriteLine("Stack underflow!");
return default(T);
}
return elements[top--];
}
public void Print()
{
Console.WriteLine("Stack elements:");
for (int i = top; i >= 0; i--)
{
Console.WriteLine(elements[i]);
}
}
}
class Program
{
static void Main(string[] args)
{
Stack<int> intStack = new Stack<int>(5);
intStack.Push(1);
intStack.Push(2);
intStack.Push(3);
intStack.Print();
Console.WriteLine("Popped item: " + intStack.Pop());
Stack<string> stringStack = new Stack<string>(3);
stringStack.Push("Hello");
stringStack.Push("World");
stringStack.Print();
Console.WriteLine("Popped item: " + stringStack.Pop());
}
}
In this example:
- We define a
Stack<T>
class that uses a generic type parameterT
. - The
Stack<T>
class can work with any data type specified when creating an instance of the class. - We demonstrate the use of the
Stack<T>
class with bothint
andstring
types. - By using generics, the
Stack
class provides flexibility and code reusability by allowing the same implementation to work with different data types.
Subtype Polymorphism (Inclusion Polymorphism):
Method Overriding: Enables objects of a derived class to be treated as objects of their base class, allowing polymorphic behavior. It occurs when a derived class inherits from a base class and provides its own implementation of certain methods, thereby overriding the behavior of the base class methods.
using System;
// Interface
public interface IShape
{
void Draw();
}
// Class implementing the interface
public class Circle : IShape
{
public void Draw()
{
Console.WriteLine("Drawing a circle");
}
}
// Another class implementing the interface
public class Rectangle : IShape
{
public void Draw()
{
Console.WriteLine("Drawing a rectangle");
}
}
class Program
{
static void Main(string[] args)
{
IShape shape1 = new Circle();
IShape shape2 = new Rectangle();
shape1.Draw(); // Calls the Draw method of Circle
shape2.Draw(); // Calls the Draw method of Rectangle
}
}
- The
Circle
class andRectangle
class both implement theIShape
interface and provide their own implementation of theDraw
method. - At runtime, we create objects of
Circle
andRectangle
classes and assign them to variables of typeIShape
.
Most of the design pattern use subtype polymorphism to implement flexible and robust class design, this is a very powerful polymorphism behavior like strategy design pattern or observer design pattern or decorator design pattern etc.
Coercion polymorphism (Casting)
Coercion polymorphism, also known as implicit conversion, refers to the automatic conversion of one data type to another by the compiler or runtime environment. This conversion occurs when it is safe and logical to do so, allowing for more flexible and convenient programming.
using System;
class Program
{
static void Main(string[] args)
{
int num1 = 10;
double num2 = num1; // Implicit conversion from int to double
Console.WriteLine("num1: " + num1); // Outputs: num1: 10
Console.WriteLine("num2: " + num2); // Outputs: num2: 10.0
}
}
In this example:
- We have an integer variable
num1
initialized with the value10
. - We assign the value of
num1
to a double variablenum2
. This assignment triggers an implicit conversion fromint
todouble
. - The compiler automatically converts the
int
value to adouble
value, as it is safe to do so without losing precision. - This implicit conversion allows us to treat
num2
as adouble
even though it was originally anint
, demonstrating coercion polymorphism.
SUMMARY
Polymorphism in object-oriented programming encompasses compile-time and runtime behaviors. Compile-time polymorphism involves method and operator overloading, allowing multiple behaviors based on parameters and operators. Runtime polymorphism, achieved through method overriding, enables a subclass to provide its own implementation of a superclass method. Additional forms include parametric polymorphism with generics, subtype polymorphism through method overriding and interfaces, and coercion polymorphism for implicit type conversions. These concepts facilitate code flexibility, reusability, and adaptability in software development.