Industrial manufacturing
Industrial Internet of Things | Industrial materials | Equipment Maintenance and Repair | Industrial programming |
home  MfgRobots >> Industrial manufacturing >  >> Industrial programming >> Java

Mastering Java Polymorphism: Concepts, Examples, and Best Practices

Java Polymorphism

Explore Java polymorphism through clear explanations and practical code examples, and understand how it enhances code flexibility and reusability.

Polymorphism—an essential pillar of object‑oriented programming—means a single entity can take on multiple forms.

That is, the same entity (method, operator, or object) can perform different operations depending on the context.


Example: Java Polymorphism

class Polygon {

  // method to render a shape
  public void render() {
    System.out.println("Rendering Polygon...");
  }
}

class Square extends Polygon {

  // renders Square
  public void render() {
    System.out.println("Rendering Square...");
  }
}

class Circle extends Polygon {

  // renders circle
  public void render() {
    System.out.println("Rendering Circle...");
  }
}

class Main {
  public static void main(String[] args) {
    
    // create an object of Square
    Square s1 = new Square();
    s1.render();

    // create an object of Circle
    Circle c1 = new Circle();
    c1.render();
  }
}

Output

Rendering Square...
Rendering Circle...

In the example above, we create a superclass: Polygon and two subclasses: Square and Circle. The render() method is inherited by both subclasses, but each overrides it to provide shape‑specific rendering logic.

The key takeaway is that render() behaves differently depending on the actual object type, which is the essence of polymorphism.


Why Polymorphism?

Polymorphism allows developers to write clean, consistent code. Instead of defining separate methods such as renderSquare() or renderCircle(), a single render() method adapts to each shape, reducing redundancy and improving maintainability.

While separate methods would work, they quickly lead to code duplication and a fragmented API. Polymorphism unifies behavior under a common contract, ensuring that any new shape can be integrated by simply overriding render().

Note: The print() method also demonstrates polymorphism—it can output char, int, String, and more without requiring multiple overloads.


We can achieve polymorphism in Java using the following techniques:

  1. Method Overriding
  2. Method Overloading
  3. Operator Overloading

Java Method Overriding

When a subclass defines a method with the same signature as a method in its superclass, the subclass method overrides the superclass method. This allows the same method name to perform different operations based on the object's runtime type.

Example 1: Polymorphism using method overriding

class Language {
  public void displayInfo() {
    System.out.println("Common English Language");
  }
}

class Java extends Language {
  @Override
  public void displayInfo() {
    System.out.println("Java Programming Language");
  }
}

class Main {
  public static void main(String[] args) {

    // create an object of Java class
    Java j1 = new Java();
    j1.displayInfo();

    // create an object of Language class
    Language l1 = new Language();
    l1.displayInfo();
  }
}

Output:

Java Programming Language
Common English Language

Here, Language serves as the base class and Java as a subclass. The displayInfo() method is defined in both classes, but each implementation delivers context‑appropriate information.

The method that executes is determined at runtime, which is why this pattern is called runtime polymorphism.

Mastering Java Polymorphism: Concepts, Examples, and Best Practices

Note: The method that is called is determined during the execution of the program. Hence, method overriding is a run-time polymorphism.


2. Java Method Overloading

Method overloading allows a class to have multiple methods with the same name but different parameter lists. The compiler resolves which method to invoke based on the arguments supplied.

void func() { ... }
void func(int a) { ... }
float func(double a) { ... }
float func(int a, float b) { ... }

This technique enables the same logical operation to accept varied inputs.

Example 3: Polymorphism using method overloading

class Pattern {

  // method without parameter
  public void display() {
    for (int i = 0; i < 10; i++) {
      System.out.print("*");
    }
  }

  // method with single parameter
  public void display(char symbol) {
    for (int i = 0; i < 10; i++) {
      System.out.print(symbol);
    }
  }
}

class Main {
  public static void main(String[] args) {
    Pattern d1 = new Pattern();

    // call method without any argument
    d1.display();
    System.out.println("\n");

    // call method with a single argument
    d1.display('#');
  }
}

Output:

**********

##########

In this example, the Pattern class contains two overloaded display() methods. The version without parameters prints ten asterisks, while the one accepting a char prints ten copies of the supplied symbol.

Because the compiler selects the method at compile time, this form of polymorphism is often referred to as compile‑time polymorphism.

Note: The method that is called is determined by the compiler. Hence, it is also known as compile-time polymorphism.


3. Java Operator Overloading

Java’s operators can exhibit different behaviors based on operand types. For example, the + operator performs numeric addition when used with numbers and string concatenation when used with strings.

Below are illustrative snippets showing how + adapts to its operands.

1. With numbers:

int a = 5;
int b = 6;

// + with numbers
int sum = a + b;  // Output = 11

2. With strings:

String first = "Java ";
String second = "Programming";

// + with strings
String name = first + second;  // Output = Java Programming

Thus, + is overloaded in Java to perform both addition and concatenation.

Note: While languages like C++ allow user-defined operator overloading, Java restricts operator overloading to built‑in operators only.


Polymorphic Variables

A variable is considered polymorphic when it can refer to objects of different types during program execution. In Java, object references enable this behavior, allowing a reference of a base type to point to instances of subclasses.

Example: Polymorphic Variables

class ProgrammingLanguage {
  public void display() {
    System.out.println("I am Programming Language.");
  }
}

class Java extends ProgrammingLanguage {
  @Override
  public void display() {
    System.out.println("I am Object-Oriented Programming Language.");
  }
}

class Main {
  public static void main(String[] args) {

    // declare an object variable
    ProgrammingLanguage pl;

    // create object of ProgrammingLanguage
    pl = new ProgrammingLanguage();
    pl.display();

    // create object of Java class
    pl = new Java();
    pl.display();
  }
}

Output:

I am Programming Language.
I am Object-Oriented Programming Language.

In this illustration, the reference pl is declared with the base type ProgrammingLanguage but is assigned objects of both the base class and its subclass. This demonstrates upcasting and runtime polymorphism.


Java

  1. Java Methods: How to Define, Call, and Use Them Effectively
  2. Java Recursion: Understanding, Examples, and Trade‑Offs
  3. Mastering Method Overriding in Java
  4. Java Annotation Types: A Comprehensive Guide to Predefined, Custom, and Meta Annotations
  5. Mastering Java's Iterator Interface: Practical Guide with Code Example
  6. Polymorphism in Java: A Comprehensive Guide with Practical Examples
  7. Mastering Java Methods: Create, Invoke, and Abstraction
  8. Java Method Overriding: Customizing Superclass Behavior
  9. Understanding Polymorphism in Java: Concepts and Practical Use
  10. Mastering Java 8: A Comprehensive Guide to Method References