Let's Talk Polymorphism in Java

...shall we?

This is an OOP concept that allows objects to take many forms. In Java, polymorphism enables flexibility and reusability in code. There are two types of polymorphism:

  • Compile Time Polymorphism (static) — achieved through method overloading and operator overloading

  • Runtime Polymorphism(dynamic) — achieved through method overriding

Compile time Polymorphism

WWe call it static because the method to be called is determined at compile time. This is done through method overloading, where multiple methods have the same name but differ in:

  • The number of parameters

  • The type of parameters

  • The sequence of parameters

      class MathOperations {
          // Method with two parameters
          int add(int a, int b) {
              return a + b;
          }
    
          // Overloaded method with three parameters
          int add(int a, int b, int c) {
              return a + b + c;
          }
    
          // Overloaded method with different parameter type
          double add(double a, double b) {
              return a + b;
          }
      }
    
      public class Main {
          public static void main(String[] args) {
              MathOperations math = new MathOperations();
    
              System.out.println(math.add(5, 10));         // Calls first method
              System.out.println(math.add(5, 10, 15));     // Calls second method
              System.out.println(math.add(5.5, 2.3));      // Calls third method
          }
      }
    

    Expected Output

      15
      30
      7.8
    

    Why is it called Static Polymorphism?

    • The method to be executed is determined by the compiler based on the arguments passed.

    • There is no decision-making at runtime.

RunTime Polymorphism

We call it dynamic because the method to be executed is determined at runtime. This is achieved through method overriding, where a subclass provides a specific implementation of a method that already exists in its parent class.

Rules for Method Overriding

  1. The method must have the same name as in the parent class.

  2. The method must have the same return type (or a subclass of the return type, known as covariant return type).

  3. The method must have the same parameter list.

  4. The method in the child class must be accessible (i.e., not more restrictive than the parent class method).

  5. The @Override annotation is optional but recommended, as it helps catch errors.

Example of Method Overriding using toString()

The toString() method is defined in the Object class (which is the superclass of all Java classes). By default, it prints the class name and memory address, but we can override it to return a meaningful string representation of an object.

    class Car {
        String brand;
        int year;

        // Constructor
        Car(String brand, int year) {
            this.brand = brand;
            this.year = year;
        }

        // Overriding the toString() method
        @Override
        public String toString() {
            return "Car Brand: " + brand + ", Year: " + year;
        }
    }

    public class Main {
        public static void main(String[] args) {
            Car myCar = new Car("Toyota", 2023);

            // Printing the object directly
            System.out.println(myCar);
        }
    }

Expected output

    Car Brand: Toyota, Year: 2023

What Exactly is Happening Here?

  • Normally, System.out.println(myCar); would print something like Car@1a2b3c4d.

  • Since we overrode toString(), it now prints a more meaningful representation of the object.

Example of Method Overriding in an Inheritance Hierarchy

    // Parent class
    class Animal {
        void makeSound() {
            System.out.println("Animal makes a sound");
        }
    }

    // Child class
    class Dog extends Animal {
        // Overriding the makeSound method
        @Override
        void makeSound() {
            System.out.println("Dog barks");
        }
    }

    // Child class
    class Cat extends Animal {
        // Overriding the makeSound method
        @Override
        void makeSound() {
            System.out.println("Cat meows");
        }
    }

    public class Main {
        public static void main(String[] args) {
            Animal myAnimal;  // Reference of type Animal

            myAnimal = new Dog();
            myAnimal.makeSound(); // Output: Dog barks

            myAnimal = new Cat();
            myAnimal.makeSound(); // Output: Cat meows
        }
    }

Expected output

    Dog barks
    Cat meows
  • Operator Overloading in Java?

    Unlike some other languages (like C++), Java does NOT support operator overloading. Java only supports method overloading and method overriding for polymorphism.

    Why Doesn’t Java Support Operator Overloading?

    • It keeps Java simple and free from complexity.

    • Overloaded operators can make the code less readable and confusing.

    • Java designers wanted to avoid ambiguities and maintain consistency.

You’ll find more of these on my account, might as well follow me🤷‍♀️