Tag: Overriding

  • Polymorphism in Object-Oriented Programming

    1. What is Polymorphism

    Polymorphism means “many forms”. “Poly” is a prefix meaning “many” and “Morphism” is derived from Greek roots meaning “form” or “shape”, and a suffix indicating a process or state.

    Polymorphism allows

    • methods or objects to behave differently based on their context.
    • objects to be treated as instances of their parent class or interface rather than their actual class.

    2. Key Concepts of Polymorphism in Java

    Java achieves polymorphism through two main mechanisms

    2.1. Compile-time polymorphism (Method Overloading) – Static Binding

    Method overloading (not strictly polymorphism) allows multiple methods with the same name but different parameters in the same class. The correct method to be called is determined at compile time based on the number and types of arguments passed.

    class MathOperations {
        // Overloaded method with two integers
        int add(int a, int b) {
            return a + b;
        }
    
        // Overloaded method with three integers
        int add(int a, int b, int c) {
            return a + b + c;
        }
    
        // Overloaded method with two doubles
        double add(double a, double b) {
            return a + b;
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            MathOperations math = new MathOperations();
            // Calls int add(int, int)
            System.out.println(math.add(5, 10));
            // Calls int add(int, int, int)       
            System.out.println(math.add(5, 10, 20));
            // Calls double add(double, double)
            System.out.println(math.add(5.5, 2.5));    
        }
    }

    2.2. Runtime polymorphism (Method Overriding) – Dynamic Binding

    Method overriding occurs when a subclass provides a specific implementation of a method that is already defined in its superclass.

    The method in the subclass must have the same name, return type, and parameters as the method in the superclass.

    The correct method to be called is determined at runtime based on the actual object type.

    class Animal {
        public void makeSound() {
            System.out.println("Animal makes a sound");
        }
    }
    
    class Dog extends Animal {
        @Override
        public void makeSound() {  // Overriding the method
            System.out.println("Dog barks");
        }
    }
    
    class Cat extends Animal {
        @Override
        public void makeSound() {  // Overriding the method
            System.out.println("Cat meows");
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            // Animal reference and Animal object
            Animal myAnimal = new Animal();
            // Upcasting - Animal reference but Dog object
            Animal myDog = new Dog();
            // Upcasting - Animal reference but Cat object
            Animal myCat = new Cat();  
    
            // Calls Animal's makeSound() at runtime
            myAnimal.makeSound();  // Output: Animal makes a sound
            // Calls Dog's makeSound() at runtime
            myDog.makeSound();     // Output: Dog barks
            // Calls Cat's makeSound() at runtime
            myCat.makeSound();     // Output: Cat meows
        }
    }

    3. Differences Between Overloading & Overriding

    FeatureMethod OverloadingMethod Overriding
    DefinitionMultiple methods with the same name but different parameters in the same class.A subclass provides a new implementation of a method defined in the superclass.
    Binding TypeCompile-time polymorphism (early binding).Runtime polymorphism (late binding).
    Method SignatureMethods have different parameters (different number, types, or order).Method signatures (name and parameters) must be exactly the same.
    Return TypeCan be different.Must be the same (or a covariant return type).
    Access ModifierNo restrictions.Cannot reduce visibility (e.g., protected method in parent cannot be private in child).
    Static MethodsCan be overloaded.Cannot be overridden (only hidden).

    4. Benefits of Polymorphism

    • Code Reusability: Polymorphism allows you to write code that can work with objects of different classes, promoting code reuse. You can write code that works with objects of a general type and then reuse it with specific types.
    • Flexibility and Extensibility: New classes can be introduced with minimal changes to existing code, making the system more flexible and extensible. You can easily add new types to your program without needing to modify existing code.
    • Maintainability – Simplified Code: Polymorphism simplifies the code by allowing a single interface to represent different types of objects. Polymorphism makes your code easier to maintain because changes to one class are less likely to affect other parts of the program.

    5. Example of Polymorphism

    5.1. Example 1 – “abstract class” and “override”

    abstract class Payment {
        abstract void processPayment(double amount);
    }
    
    class CreditCardPayment extends Payment {
        @Override
        void processPayment(double amount) {
            System.out.println("Processing credit card payment of $" + amount);
        }
    }
    
    class PayPalPayment extends Payment {
        @Override
        void processPayment(double amount) {
            System.out.println("Processing PayPal payment of $" + amount);
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            Payment payment1 = new CreditCardPayment();
            Payment payment2 = new PayPalPayment();
    
            // Calls CreditCardPayment's processPayment()
            payment1.processPayment(100.50);
            // Calls PayPalPayment's processPayment()  
            payment2.processPayment(75.25);   
        }
    }

    In this example, the Payment class is abstract, enforcing a contract that all subclasses must follow. Polymorphism allows us to call processPayment() on different types of payments without changing the calling code.

    5.2. Example 2 – “interface” and “implements”

    interface Shape {
        double getArea();
    }
    
    class Circle implements Shape {
        // ...
    }
    
    class Rectangle implements Shape {
        // ...
    }
    
    // ...
    
    Shape[] shapes = new Shape[10];
    shapes[0] = new Circle();
    shapes[1] = new Rectangle();
    // ...
    
    for (Shape shape : shapes) {
        double area = shape.getArea(); // Polymorphism!
        // ...
    }

    In this example, you can treat all the shapes in the array as Shape objects, even though they are actually different types of shapes. This is polymorphism in action! You can call the getArea method on any shape, and it will return the correct area for that specific shape.

    6. Summay

    Polymorphism in Java allows methods to do different things based on the object that invokes them, either through method overloading (compile-time polymorphism) or method overriding (run-time polymorphism). This concept enhances the flexibility, reusability, and maintainability of the code.

    Polymorphism is a crucial concept in OOP. It allows for more flexible and maintainable code by enabling objects of different classes to be treated as objects of a common type. This is achieved through mechanisms like method overriding and late binding.

    Polymorphism allows methods and objects to take multiple forms, improving flexibility and code reusability.

    • Method Overloading occurs at compile time (same method name, different parameters).
    • Method Overriding occurs at runtime (same method signature, different implementation in a subclass).
    • Polymorphism enables better scalability, maintainability, and code organization in Java applications.