Java Core: Inheritance and Polymorphism - Method Overriding
Method overriding is a core concept in object-oriented programming, specifically within the realms of inheritance and polymorphism. It allows a subclass to provide a specific implementation of a method that is already defined in its superclass. This is a powerful mechanism for customizing behavior and achieving polymorphism.
What is Method Overriding?
- Definition: Method overriding occurs when a subclass provides its own implementation of a method that is already defined in its superclass. The method must have the same name, return type, and parameter list (signature) as the method in the superclass.
- Purpose: To allow a subclass to modify or extend the behavior inherited from its superclass. It's about replacing the superclass's implementation with a more specialized one.
- Key Requirements:
- Inheritance: Overriding can only happen when a class inherits from another class (using the
extendskeyword). - Same Signature: The method in the subclass must have the exact same name, return type, and parameter list as the method in the superclass. The order and types of parameters are crucial.
- Accessibility: The overriding method in the subclass must have at least the same access modifier as the overridden method in the superclass. (e.g.,
publiccan overrideprotected, butprotectedcannot overridepublic). @OverrideAnnotation (Recommended): Using the@Overrideannotation is highly recommended. It tells the compiler that you intend to override a method. If the method doesn't actually override anything (e.g., a typo in the method name), the compiler will generate an error, helping you catch mistakes early.
- Inheritance: Overriding can only happen when a class inherits from another class (using the
Example
// Superclass (Base Class)
class Animal {
public void makeSound() {
System.out.println("Generic animal sound");
}
}
// Subclass (Derived Class)
class Dog extends Animal {
@Override // Good practice!
public void makeSound() {
System.out.println("Woof!");
}
}
// Subclass (Derived Class)
class Cat extends Animal {
@Override // Good practice!
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!
// Polymorphism in action
Animal animalDog = new Dog(); // Upcasting
animalDog.makeSound(); // Output: Woof! (Dog's implementation)
Animal animalCat = new Cat(); // Upcasting
animalCat.makeSound(); // Output: Meow! (Cat's implementation)
}
}
Explanation:
Animalis the superclass with amakeSound()method.DogandCatare subclasses ofAnimal.- Both
DogandCatoverride themakeSound()method, providing their own specific implementations. - In the
mainmethod, whenmakeSound()is called on aDogobject, theDog's implementation is executed. Similarly, forCat. - The lines
Animal animalDog = new Dog();andAnimal animalCat = new Cat();demonstrate polymorphism. We're assigning aDogobject to anAnimalreference. WhenanimalDog.makeSound()is called, the actual object type (Dog) determines whichmakeSound()method is executed. This is runtime polymorphism.
super Keyword
The super keyword can be used within the overridden method in the subclass to call the superclass's implementation of the method. This is useful if you want to extend the superclass's behavior rather than completely replacing it.
class Animal {
public void makeSound() {
System.out.println("Generic animal sound");
}
}
class Dog extends Animal {
@Override
public void makeSound() {
super.makeSound(); // Call the superclass's makeSound()
System.out.println("Woof!"); // Add Dog-specific behavior
}
}
public class Main {
public static void main(String[] args) {
Dog dog = new Dog();
dog.makeSound(); // Output: Generic animal sound\nWoof!
}
}
In this example, the Dog's makeSound() method first calls the Animal's makeSound() method using super.makeSound(), and then adds its own "Woof!" output.
Why Use Method Overriding?
- Polymorphism: Enables you to treat objects of different classes in a uniform way through a common interface (the superclass).
- Code Reusability: Inherit common behavior from the superclass and customize it in subclasses.
- Flexibility and Extensibility: Easily add new subclasses with specialized behavior without modifying the superclass.
- Maintainability: Changes to the superclass's behavior can be propagated to subclasses (unless overridden).
Method Overriding vs. Method Overloading
It's important to distinguish method overriding from method overloading:
| Feature | Method Overriding | Method Overloading |
|---|---|---|
| Relationship | Occurs in inheritance (subclass-superclass) | Occurs within the same class |
| Purpose | To provide a specific implementation in a subclass | To provide multiple methods with the same name but different parameters |
| Parameters | Same signature (name, return type, parameters) | Different parameter lists (number, types, order) |
| Return Type | Must be the same | Can be different |
| Keyword | @Override (recommended) |
None |
In summary, method overriding is a fundamental concept in object-oriented programming that allows subclasses to customize inherited behavior, enabling polymorphism and promoting code reusability and flexibility. Using the @Override annotation is a best practice to ensure correctness and maintainability.