zaro

What is Overriding in Java?

Published in Java Polymorphism 5 mins read

Overriding in Java is a powerful concept within object-oriented programming that allows a subclass to provide a specific implementation for a method that is already defined in its superclass. It is a cornerstone of polymorphism, enabling dynamic behavior based on the actual object type at runtime.

Understanding Method Overriding

The ability of a subclass to override a method is crucial because it allows a class to inherit methods from a superclass whose behavior is "close enough" to what is needed, and then to modify or specialize that behavior as required. For a method to successfully override another:

  • It must have the same name as the method it overrides.
  • It must have the same number and type of parameters (i.e., the same method signature).
  • It must have the same return type (or a covariant return type in Java 5 and later).

When an overridden method is called on an object, Java's runtime system determines which version of the method (from the superclass or the subclass) to execute based on the actual type of the object, not the type of the reference variable. This is known as runtime polymorphism or dynamic method dispatch.

Key Rules and Characteristics of Overriding

For a method to be correctly overridden, several rules must be followed:

  • Method Signature: The overriding method must have the exact same signature (name and parameter list) and return type as the method in the superclass.
  • Access Modifier: The access level of the overriding method cannot be more restrictive than the overridden method. For example, a public method can be overridden by a public method, but a protected method cannot override a public method.
  • Non-Private/Non-Final: Methods declared as private or final in the superclass cannot be overridden. private methods are not inherited, and final methods are designed to prevent modification.
  • Static Methods: static methods cannot be overridden; they are "hidden" if a subclass defines a static method with the same signature.
  • Constructors: Constructors cannot be overridden as they are special methods used for object initialization and are not inherited in the traditional sense.
  • Abstract Methods: If a superclass declares an abstract method, the first concrete (non-abstract) subclass must provide an implementation for that method, effectively overriding it.
  • Checked Exceptions: The overriding method cannot declare checked exceptions that are new or broader than those declared by the overridden method. It can declare fewer or no checked exceptions, or the same checked exceptions, or subclasses of the exceptions declared by the overridden method.

The @Override Annotation

It is highly recommended to use the @Override annotation when overriding a method. This annotation provides a compile-time check to ensure that the method you intend to override actually exists in the superclass with the correct signature. If it doesn't, the compiler will flag an error, helping prevent common mistakes like typos in method names or parameter lists.

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

class Dog extends Animal {
    @Override // Good practice to use this annotation
    public void makeSound() {
        System.out.println("Dog barks: Woof! Woof!");
    }
}

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

public class OverridingDemo {
    public static void main(String[] args) {
        Animal myAnimal = new Animal();
        Animal myDog = new Dog(); // Polymorphic reference
        Animal myCat = new Cat();

        myAnimal.makeSound(); // Output: Animal makes a sound
        myDog.makeSound();    // Output: Dog barks: Woof! Woof!
        myCat.makeSound();    // Output: Cat meows: Meow!
    }
}

In the example above, the Dog and Cat classes override the makeSound() method inherited from the Animal class, providing their own specific implementations. When myDog.makeSound() is called, even though myDog is an Animal reference, the version of makeSound() from the Dog class is executed because the actual object type is Dog.

Overriding vs. Overloading

It's important to distinguish method overriding from method overloading, as they are often confused but serve different purposes:

Feature Method Overriding Method Overloading
Concept Providing a specific implementation for an inherited method. Defining multiple methods with the same name but different parameters within the same class.
Relationship Parent-child relationship (inheritance). Within a single class or its hierarchy.
Method Signature Must be identical (name, parameters, return type). Must differ in parameters (number, type, or order).
Polymorphism Runtime (Dynamic) Polymorphism. Compile-time (Static) Polymorphism.
Return Type Must be the same or covariant. Can be different.

Benefits and Use Cases

Method overriding is fundamental to creating robust and flexible Java applications:

  • Code Reusability and Extensibility: It allows subclasses to reuse the general behavior defined in a superclass while providing specialized implementations where necessary.
  • Specific Behavior: Enables objects of different subclasses to respond uniquely to the same method call, which is the essence of polymorphism.
  • Frameworks and APIs: Widely used in Java frameworks (e.g., Spring, Android) and standard APIs (e.g., toString(), equals(), hashCode() methods from Object class) where users are expected to provide custom implementations of inherited methods.
  • Achieving Polymorphism: It's a key mechanism for achieving runtime polymorphism, making code more flexible and adaptable to changes.

For further reading on inheritance and methods, refer to the Java™ Tutorials on Inheritance.