Understanding Java Polymorphism

Concept explanation of how objects can take multiple forms through inheritance and interfaces

java (1.0+) 2025-11-03 polymorphism oop inheritance concepts

Description

Polymorphism is one of the four fundamental principles of object-oriented programming. It allows objects of different types to be treated through the same interface, enabling code to work with objects of multiple types.

Key Concepts

What is Polymorphism?

  • Poly = many, morph = forms
  • Same interface, different implementations
  • Ability of an object to take multiple forms
  • Enables “write once, use many” approach

Types of Polymorphism

Compile-time Polymorphism (Method Overloading)

  • Multiple methods with same name but different parameters
  • Resolved at compile time
  • Based on method signature (number, type, order of parameters)
  • Return type alone cannot distinguish overloaded methods

Runtime Polymorphism (Method Overriding)

  • Subclass provides specific implementation of parent method
  • Resolved at runtime based on actual object type
  • Requires inheritance or interface implementation
  • Enables dynamic method dispatch

Method Binding

  • Static binding: Method call resolved at compile time (overloading, static methods, final methods)
  • Dynamic binding: Method call resolved at runtime (overriding instance methods)

Virtual Method Invocation

  • Java uses virtual method invocation for overridden methods
  • The JVM calls the method based on the actual object type, not the reference type
  • This is why overriding works - the runtime type determines which method executes

Benefits

  • Flexibility: Code can work with multiple types
  • Extensibility: Add new types without modifying existing code
  • Maintainability: Centralize common logic
  • Code reuse: Share interfaces while allowing different implementations

Best Practices

  • Use interfaces or abstract classes for polymorphic behavior
  • Favor composition over inheritance when appropriate
  • Use @Override annotation for clarity
  • Keep method signatures consistent in polymorphic hierarchies
  • Document expected behavior in base classes/interfaces

Common Patterns

  • Strategy pattern: Different algorithms at runtime
  • Template method: Define algorithm skeleton, let subclasses fill steps
  • Factory pattern: Create objects without specifying exact class
  • Dependency injection: Inject implementations through interfaces

Code

RAW
// Base classclass Animal {    public void makeSound() {        System.out.println("Animal makes a sound");    }}// Subclassesclass Dog extends Animal {    @Override    public void makeSound() {        System.out.println("Dog barks");    }}class Cat extends Animal {    @Override    public void makeSound() {        System.out.println("Cat meows");    }}// Runtime polymorphismAnimal animal1 = new Dog();Animal animal2 = new Cat();animal1.makeSound(); // "Dog barks" - calls Dog's methodanimal2.makeSound(); // "Cat meows" - calls Cat's method// Polymorphic arrayAnimal[] animals = {new Dog(), new Cat(), new Animal()};for (Animal animal : animals) {    animal.makeSound(); // Calls appropriate method for each}// Compile-time polymorphism (Method Overloading)class Calculator {    public int add(int a, int b) {        return a + b;    }        public double add(double a, double b) {        return a + b;    }        public int add(int a, int b, int c) {        return a + b + c;    }}Calculator calc = new Calculator();int sum1 = calc.add(5, 3);        // Calls int versiondouble sum2 = calc.add(5.5, 3.2); // Calls double versionint sum3 = calc.add(1, 2, 3);     // Calls three-parameter version// Interface polymorphisminterface Shape {    double area();}class Circle implements Shape {    private double radius;        public Circle(double radius) {        this.radius = radius;    }        @Override    public double area() {        return Math.PI * radius * radius;    }}class Rectangle implements Shape {    private double width, height;        public Rectangle(double width, double height) {        this.width = width;        this.height = height;    }        @Override    public double area() {        return width * height;    }}// Polymorphic usageShape[] shapes = {    new Circle(5.0),    new Rectangle(4.0, 6.0)};for (Shape shape : shapes) {    System.out.println("Area: " + shape.area());}

Comments

No comments yet. Be the first to comment!