Inheritance and Polymorphism

Dive deeper into inheritance, its benefits, and different types (single, multiple, multilevel). Understand polymorphism (method overriding and overloading) and its applications.


Method Overriding in Java

What is Method Overriding?

Method overriding is a key concept in object-oriented programming that allows a subclass to provide a specific implementation for a method that is already defined in its superclass. It allows a subclass to inherit behavior from a superclass but then modify or extend that behavior to suit its specific needs. In essence, it provides a way to customize the inherited functionality.

Detailed Explanation of Method Overriding in Inheritance

When a subclass overrides a method, it provides its own version of the method's implementation. This new implementation takes precedence over the superclass's version when the method is called on an object of the subclass. The method signature (name, return type, and parameter list) must be exactly the same in both the superclass and the subclass for overriding to occur. If the method signature is different, it's considered method overloading, not overriding.

Here's a breakdown of the key aspects:

  • Same Method Signature: The overriding method in the subclass must have the same name, return type, and parameter list as the overridden method in the superclass.
  • Inheritance: Overriding only occurs within an inheritance hierarchy (i.e., between a superclass and its subclass).
  • `@Override` Annotation: While not mandatory, it's highly recommended to use the `@Override` annotation above the overriding method in the subclass. This annotation serves as a compiler directive to check that you are indeed overriding a method and not accidentally creating a new one with a slightly different signature. It can prevent subtle errors.
  • Access Modifiers: The overriding method in the subclass cannot have a more restrictive access modifier than the overridden method in the superclass. For example, if the superclass method is `protected`, the subclass method can be `protected` or `public`, but not `private` or default (package-private). It can be less restrictive, allowing more access.
  • `super` Keyword: Within the overriding method in the subclass, you can use the `super` keyword to call the overridden method in the superclass. This allows you to extend the functionality of the superclass method while also adding your own specific behavior.

Let's illustrate this with a Java code example:

 class Animal {
    public void makeSound() {
        System.out.println("Generic animal sound");
    }
}

class Dog extends Animal {
    @Override
    public void makeSound() {
        System.out.println("Woof!");
    }
}

class Cat extends Animal {
    @Override
    public void makeSound() {
        System.out.println("Meow!");
    }
}


public class Main {
    public static void main(String[] args) {
        Animal animal = new Animal();
        Dog dog = new Dog();
        Cat cat = new Cat();

        animal.makeSound(); // Output: Generic animal sound
        dog.makeSound();    // Output: Woof!
        cat.makeSound();    // Output: Meow!

        Animal animalDog = new Dog(); // Polymorphism example
        animalDog.makeSound(); // Output: Woof! (calls Dog's makeSound)
    }
} 

In this example:

  • `Animal` is the superclass, and it has a method called `makeSound()`.
  • `Dog` and `Cat` are subclasses of `Animal`.
  • Both `Dog` and `Cat` override the `makeSound()` method to provide their own specific implementations (barking and meowing, respectively).
  • When `makeSound()` is called on a `Dog` object, the `Dog`'s version of the method is executed. The same applies to the `Cat` object.
  • The `Animal animalDog = new Dog();` line demonstrates polymorphism. Even though `animalDog` is declared as an `Animal`, it is instantiated as a `Dog`. Therefore, when `animalDog.makeSound()` is called, the `Dog`'s `makeSound()` method is executed. This is because the actual object type at runtime determines which overridden method is called.

Benefits of Method Overriding

Method overriding provides several important benefits:

  • Code Reusability: Subclasses inherit the basic structure and functionality from the superclass, reducing code duplication.
  • Customization: Subclasses can tailor the inherited behavior to their specific needs, providing specialized implementations.
  • Polymorphism: Overriding enables polymorphism, which allows objects of different classes to be treated as objects of a common type. This is crucial for writing flexible and extensible code. As demonstrated in the example, polymorphism allows you to treat a `Dog` as an `Animal` and still invoke its specialized `makeSound()` method.
  • Abstraction: Method overriding allows you to define abstract behavior in the superclass and then provide concrete implementations in the subclasses, promoting abstraction and modularity.