Christmas Special : Upto 40% OFF! + 2 free courses  - SCHEDULE CALL

sddsfsf

Understanding Polymorphism in Object-Oriented Programming: Exploring Types and Examples

 

Introduction:

Polymorphism in object oriented programming is essential because it improves code reusability, flexibility, and maintainability. It enables the treatment of objects of distinct classes as objects of a single superclass or interface. In this blog, we will look at the notion of polymorphism in OOPs (Object-Oriented Programming), its kinds - compile-time polymorphism (method overloading) and runtime polymorphism (method overriding), and show what is polymorphism in oops with examples to help you understand these concepts. Evolve as an expert software tester to gain in-depth knowledge and become a master in this field. 

Two Types of Polymorphism in OOPs 

Compile-Time Polymorphism / Method Overloading

Compile-time polymorphism, also known as static polymorphism, is a mechanism that is accomplished through method overloading. This particular form of polymorphism allows for multiple methods within a class that share the same name but possess different parameters. When invoking a method with compile-time polymorphism, the compiler determines which specific method to execute based on factors such as the number, order, and types of arguments provided during the invocation process. By leveraging this feature, developers can write more flexible and expressive code by reusing method names while adapting them to handle varying input scenarios.

Method overloading is a feature in object-oriented programming that allows multiple methods with the same name but different parameters to coexist within a class. This enables developers to create more flexible and expressive code by providing different ways to invoke the same method depending on the specific requirements.One of the main advantages of compile-time polymorphism, achieved through method overloading, is its ability to improve code readability and maintainability. Developers can easily understand their purpose using descriptive names for overloaded methods without relying on comments or documentation. For example, imagine having a class called "MathUtils" that contains various overloaded methods for calculating areas:

```java
public class MathUtils {
    public double calculateArea(double radius) {
        // Calculate area of circle
    }
    public double calculateArea(double length, double width) {
        // Calculate area of rectangle
    }
    public double calculateArea(double base, double height) {
      // Calculate area of triangle
    }
}
```

By employing this approach, the determination of which method to utilize becomes evident by considering the quantity and types of arguments provided during invocation. For instance, if we desire to compute the area of a rectangle with sides measuring 5 units each, we would invoke the method `calculateArea(5, 5)`.For facilitating clarity in method selection based on argument specifications, compile-time polymorphism offers another advantage: type safety. This means that during compilation, potential type-related errors are identified and flagged by the compiler itself before execution occurs.

By catching these issues early on, developers can ensure that their code adheres to expected data types and minimize runtime errors caused by incompatible or mismatched variables.Since overloaded methods have different parameter signatures (i.e., different combinations of argument types), it helps prevent accidental misuse or incorrect invocations at compile time. The compiler ensures that only valid method calls are made based on the available choices.

Compile-time polymorphism also promotes code reusability by allowing developers to define similar behavior across multiple scenarios without duplicating code logic. For instance, if you have an application that needs to convert temperature values between Celsius and Fahrenheit scales in various parts throughout your codebase:

```java

public class TemperatureConverter {
    public static double convertToFahrenheit(double celsius) {
        // Convert Celsius to Fahrenheit
    }
    public static double convertToCelsius(double fahrenheit) {
     // Convert Fahrenheit to Celsius

    }
}

```

By employing method overloading for the `convert` method, you can effectively reuse the conversion logic without the need to create separate methods for each possible combination of temperature conversions.

However, it is crucial to understand that when overloading methods, only the compiler considers their signatures (i.e., names and parameters). This means that while multiple methods may share the same name, they must have different parameter lists to be distinguished from one another during compilation.The return type or exceptions a method throws do not affect its signature. Therefore, two methods with different return types cannot be overloaded solely based on their return type difference.

Example:

Let's consider an example where we have a class called "Calculator" that performs arithmetic operations such as addition:

```java
public class Calculator {
public int add(int num1, int num2) {
     return num1 + num2;
}
public double add(double num1, double num2) {
     return num1 + num2;
}
}
```

In this example, we have two overloaded methods named "add." The first method accepts two integers as parameters, while the second takes two doubles. During compilation time, when invoking `add`, if we pass integer values like `add(5, 10)`, the compiler resolves it to call the first `add` method with integer parameters. Similarly, passing decimal values like `add(3.14 , 6.28)` invokes the second `add` method with double parameters.

Method overloading allows us to create methods with the same name but different parameter lists, providing flexibility and code readability. It simplifies method naming conventions by using a single name for similar operations. However, it's important to note that only changing the return type of a method does not constitute overloading.

Pros:

  • Improved code readability as similar operations are grouped under one method name.
  • Flexibility in handling different types of polymorphism in oops without explicitly converting them.
  • Enhances code maintainability by reducing redundant method names.

Cons:

  • This can lead to clarity if fewer overloaded methods have similar signatures.
  • Requires careful consideration when designing overloaded methods to avoid ambiguity.

Run-Time Polymorphism or Method Overriding

Run-time polymorphism is achieved through method overriding. To understand run-time polymorphism, consider an example involving an Animal (superclass) and a Dog (subclass). In this scenario, the Dog class overrides a method called makeSound() defined in its superclass, Animal.

When we create an instance of the Dog class and assign it to a variable of type Animal, such as:

Animal animal = new Dog();

At runtime, the decision on which overridden method to execute is based on the actual type of the object being referred to. In this case, even though we have assigned a Dog object to an Animal reference variable when we invoke the makeSound() method on that variable:

animal.makeSound();

The implementation of makeSound() in the Dog class will be executed instead of in the Animal class. This is because run-time polymorphism allows different methods to be executed based on the actual type of an object rather than just its reference type.

This concept is fundamental in object-oriented programming, enabling more dynamic and flexible behavior within our programs. By overriding methods in subclasses, we can provide specialized implementations while maintaining a common interface through inheritance.

```
Animal myDog = new Dog();
```

Even though we are assigning an instance of Dog to a variable of type Animal, thanks to inheritance, this assignment is valid because every dog is also considered an animal. Now, if we call the makeSound() method using this reference variable:

```
myDog.makeSound();
```

The output will be "Dog makes sound". This happens because, at runtime, Java determines that the actual type of myDog is Dog (even though it was declared as Animal), so it executes the overridden version of makeSound() defined in the Dog class.

This behavior showcases one important aspect of run-time polymorphism: even though we have used an Animal reference variable to hold our object, Java still uses dynamic binding during runtime to determine which specific version of makeSound() should be called based on its actual type.

Run-time polymorphism becomes particularly useful when dealing with collections or arrays containing objects from different subclasses but sharing some common superclasses. For example:

```
Animal[] animals = new Animal[2];
animals[0] = new Cat();
animals[1] = new Dog();
for (Animal animal : animals) {
    animal.makeSound();
}
```

Here, we have an array of Animal references that can hold objects from different subclasses. During the iteration, when calling makeSound() on each element, Java will dynamically determine which version of makeSound() to execute based on the actual type of each object.run-time polymorphism allows for flexible and dynamic method invocation in object-oriented programming. By overriding methods in subclasses, we can provide specialized implementations and let Java decide at runtime which implementation to execute based on the actual type of an object. Consider an example where we have a class hierarchy consisting of a base class, "Animal," and two subclasses - "Dog" and "Cat." This feature becomes particularly useful when dealing with collections or arrays containing objects from these subclasses but sharing the common superclass.

By utilizing run-time polymorphism, we can write more generic code without needing to check types or cast objects explicitly. This allows for greater flexibility in handling diverse objects within a collection or array, as the appropriate overridden methods will be invoked based on the actual type of each object during runtime.

Each subclass overrides the `makeSound` method from the parent class:

```java
public class Animal {
public void makeSound() {
System.out.println("Generic animal sound");
}
}
public class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("Woof!");
}
}
public class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("Meow!");
}
}
```

In this example, we create objects of both the `Dog` and `Cat` classes and invoke their respective `makeSound` methods. At runtime, Java determines which version of `makeSound` should be executed based on the object type. 

Method overriding allows us to provide specialized implementations in subclasses while maintaining common behavior defined in superclasses. It facilitates code extensibility by allowing new functionalities specific to each subclass without modifying existing code.

Pros:

  • Enables code extensibility by adding new behaviors to existing classes without modifying their structure.
  • Supports dynamic binding, allowing different objects with similar behavior but distinct implementations to be treated uniformly.
  • Promotes code reusability as common functionalities can be implemented once in superclasses and inherited by multiple subclasses.

Cons:

  • Can introduce complexity if not used judiciously, leading to confusion about which overridden method will be executed.
  • Requires careful consideration when modifying an overridden method's behavior as it may impact other parts of the program that rely on it.

Conclusion

This blog helps you understand and define polymorphism in oops. Polymorphism is an essential concept in OOPs that enables flexible coding practices by treating objects interchangeably within inheritance hierarchies or interfaces. Compile-time polymorphism (method overloading) and runtime polymorphism (method overriding) are two different types of polymorphism in oops that enhance code reusability, readability, and maintainability. By understanding these concepts and their practical examples, you can leverage the power of polymorphism to write efficient and scalable Java programs. Enhance your career with a QA Software Testing Training Course for beginners and professionals.

Trending Courses

Cyber Security icon

Cyber Security

  • Introduction to cybersecurity
  • Cryptography and Secure Communication 
  • Cloud Computing Architectural Framework
  • Security Architectures and Models
Cyber Security icon1

Upcoming Class

13 days 04 Jan 2025

QA icon

QA

  • Introduction and Software Testing
  • Software Test Life Cycle
  • Automation Testing and API Testing
  • Selenium framework development using Testing
QA icon1

Upcoming Class

6 days 28 Dec 2024

Salesforce icon

Salesforce

  • Salesforce Configuration Introduction
  • Security & Automation Process
  • Sales & Service Cloud
  • Apex Programming, SOQL & SOSL
Salesforce icon1

Upcoming Class

8 days 30 Dec 2024

Business Analyst icon

Business Analyst

  • BA & Stakeholders Overview
  • BPMN, Requirement Elicitation
  • BA Tools & Design Documents
  • Enterprise Analysis, Agile & Scrum
Business Analyst icon1

Upcoming Class

5 days 27 Dec 2024

MS SQL Server icon

MS SQL Server

  • Introduction & Database Query
  • Programming, Indexes & System Functions
  • SSIS Package Development Procedures
  • SSRS Report Design
MS SQL Server icon1

Upcoming Class

5 days 27 Dec 2024

Data Science icon

Data Science

  • Data Science Introduction
  • Hadoop and Spark Overview
  • Python & Intro to R Programming
  • Machine Learning
Data Science icon1

Upcoming Class

12 days 03 Jan 2025

DevOps icon

DevOps

  • Intro to DevOps
  • GIT and Maven
  • Jenkins & Ansible
  • Docker and Cloud Computing
DevOps icon1

Upcoming Class

4 days 26 Dec 2024

Hadoop icon

Hadoop

  • Architecture, HDFS & MapReduce
  • Unix Shell & Apache Pig Installation
  • HIVE Installation & User-Defined Functions
  • SQOOP & Hbase Installation
Hadoop icon1

Upcoming Class

6 days 28 Dec 2024

Python icon

Python

  • Features of Python
  • Python Editors and IDEs
  • Data types and Variables
  • Python File Operation
Python icon1

Upcoming Class

5 days 27 Dec 2024

Artificial Intelligence icon

Artificial Intelligence

  • Components of AI
  • Categories of Machine Learning
  • Recurrent Neural Networks
  • Recurrent Neural Networks
Artificial Intelligence icon1

Upcoming Class

13 days 04 Jan 2025

Machine Learning icon

Machine Learning

  • Introduction to Machine Learning & Python
  • Machine Learning: Supervised Learning
  • Machine Learning: Unsupervised Learning
Machine Learning icon1

Upcoming Class

5 days 27 Dec 2024

 Tableau icon

Tableau

  • Introduction to Tableau Desktop
  • Data Transformation Methods
  • Configuring tableau server
  • Integration with R & Hadoop
 Tableau icon1

Upcoming Class

6 days 28 Dec 2024