Method Overriding is an important feature of Object-Oriented Programming (OOP) in Java that allows a subclass (child class) to provide its own implementation of a method that is already defined by its superclass (parent class).
Method overriding provides a way to achieve runtime polymorphism in Java.
In this tutorial, we will cover:
- What is Method Overriding?
- Rules for Method Overriding
- Using the @Override Annotation
- Difference Between Method Overriding and Overloading
- Examples of Method Overriding
- Best Practices for Method Overriding
- Examples and Use Cases
Let’s dive into each topic with code examples!
Table of Contents
1. What is Method Overriding?
Method overriding occurs when a subclass provides its specific implementation of a method that is already defined by its parent class.
The method in the subclass must have the same signature (method name, return type, and parameters) as the method in the parent class.
Key Features:
- Runtime Polymorphism: Method overriding enables runtime polymorphism, allowing Java to determine the method to call at runtime.
- Same Method Signature: The overridden method in the child class must have the same name, return type, and parameters as the method in the parent class.
- Subclass-Specific Implementation: The child class can customize the behavior of the method.
2. Rules for Method Overriding
- The method in the child class must have the same name, return type, and parameter list as the method in the parent class.
- The access modifier of the overriding method in the child class cannot be more restrictive than the method in the parent class. For example, if the parent method is public, the child method must also be public or more accessible.
- The overriding method can throw fewer or no exceptions but not more exceptions than the overridden method in the parent class.
- Static methods and constructors cannot be overridden.
3. Using the @Override Annotation
The @Override annotation is used to indicate that a method is intended to override a method in a parent class.
While it is not mandatory, it helps prevent errors by alerting the developer if the method does not correctly override the parent method.
Example: Basic Method Overriding
class Animal { // Parent class method public void sound() { System.out.println("Animal makes a sound."); } } class Dog extends Animal { // Overriding the sound() method in the subclass @Override public void sound() { System.out.println("Dog barks."); } } public class MethodOverridingExample { public static void main(String[] args) { Animal animal = new Animal(); // Animal reference, Animal object animal.sound(); // Output: Animal makes a sound. Animal dog = new Dog(); // Animal reference, Dog object (polymorphism) dog.sound(); // Output: Dog barks. (method overridden) } }
Explanation:
- The method sound() in the Dog class overrides the sound() method in the Animal class.
- The @Override annotation ensures that this method is overriding the method in the parent class.
- Polymorphism: The method sound() is called on an Animal reference that holds a Dog object, and the Dog's version of the method is invoked at runtime.
4. Difference Between Method Overriding and Overloading
- Method Overriding occurs when a subclass provides a specific implementation of a method that already exists in its superclass.
- Method Overloading occurs when two or more methods in the same class share the same name but have different parameter lists.
Example: Overriding vs. Overloading
class Animal { public void sound() { System.out.println("Animal makes a sound."); } // Overloading: Two methods with the same name but different parameters public void sound(String type) { System.out.println("Animal makes a sound: " + type); } } class Dog extends Animal { // Overriding: Same method signature as in Animal class @Override public void sound() { System.out.println("Dog barks."); } } public class OverloadingVsOverriding { public static void main(String[] args) { Dog dog = new Dog(); dog.sound(); // Output: Dog barks. dog.sound("loud bark"); // Output: Animal makes a sound: loud bark (overloaded method) } }
Explanation:
- Overloading: The sound() method is overloaded with a different parameter list (sound(String)), which allows different method implementations depending on the arguments passed.
- Overriding: The Dog class overrides the sound() method from the Animal class, replacing its behavior.
5. Examples of Method Overriding
Example 1: Overriding a Parent Class Method
class Vehicle { public void startEngine() { System.out.println("Vehicle engine started."); } } class Car extends Vehicle { @Override public void startEngine() { System.out.println("Car engine started with ignition key."); } } public class VehicleExample { public static void main(String[] args) { Vehicle vehicle = new Vehicle(); vehicle.startEngine(); // Output: Vehicle engine started. Vehicle car = new Car(); car.startEngine(); // Output: Car engine started with ignition key. (overridden method) } }
Example 2: Calling the Parent Class Method from the Overriding Method
You can use the super keyword to call the method from the parent class within the overriding method.
class Animal { public void eat() { System.out.println("Animal is eating."); } } class Cat extends Animal { @Override public void eat() { super.eat(); // Call parent class method System.out.println("Cat is eating fish."); } } public class CallParentMethodExample { public static void main(String[] args) { Cat cat = new Cat(); cat.eat(); // Output: // Animal is eating. // Cat is eating fish. } }
Explanation:
- The super.eat() method calls the eat() method from the Animal class, and the Cat class adds its custom behavior by overriding the eat() method.
6. Best Practices for Method Overriding
1. Always Use the @Override Annotation
Using the @Override annotation helps ensure that you are overriding the method from the parent class correctly. It helps avoid subtle bugs caused by method signature mismatches.
2. Follow the Access Modifier Rule
When overriding a method, the access modifier of the overridden method cannot be more restrictive than that of the parent class method. For example, if the parent method is protected, the child class method can be protected or public, but not private.
3. Preserve Method Signatures
Make sure the overriding method has the exact same signature (name, parameters, and return type) as the parent class method. Java will throw a compile-time error if the signatures do not match.
7. Examples and Use Cases
Example 1: Overriding toString() Method
The toString() method is defined in the Object class, and it is common to override it to provide a meaningful string representation of an object.
class Person { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } // Overriding the toString() method @Override public String toString() { return "Person{name='" + name + "', age=" + age + "}"; } } public class ToStringOverrideExample { public static void main(String[] args) { Person person = new Person("Alice", 30); System.out.println(person.toString()); // Output: Person{name='Alice', age=30} } }
Example 2: Overriding equals() and hashCode() Methods
To correctly compare objects for equality, you need to override both the equals() and hashCode() methods. These methods are defined in the Object class.
class Person { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } // Overriding equals() method @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null || getClass() != obj.getClass()) return false; Person person = (Person) obj; return age == person.age && name.equals(person.name); } // Overriding hashCode() method @Override public int hashCode() { return name.hashCode() + age; } } public class EqualsAndHashCodeExample { public static void main(String[] args) { Person person1 = new Person("Bob", 25); Person person2 = new Person("Bob", 25); System.out.println(person1.equals(person2)); // Output: true } }
Explanation:
- equals() is overridden to check if two Person objects are equal based on their name and age values.
- hashCode() is overridden to maintain the contract between equals() and hashCode(), which ensures that equal objects have the same hash code.
Summary of Key Concepts for Method Overriding
Concept | Description |
---|---|
Method Overriding | Allows a subclass to provide its own implementation of a method that is already defined in its superclass. |
Runtime Polymorphism | Method overriding enables runtime polymorphism, where the method to be executed is determined at runtime. |
@Override Annotation | Used to indicate that a method is intended to override a method in a superclass. |
super Keyword | Used to call the parent class's method from within the overriding method. |
Access Modifier Rule | The overriding method cannot have a more restrictive access modifier than the method in the parent class. |
Overriding vs Overloading | Overriding involves redefining methods with the same signature, while overloading involves different signatures. |
Conclusion
In Java, method overriding allows subclasses to customize or replace the behavior of methods defined in their parent classes, enabling runtime polymorphism. In this tutorial, we covered:
- The rules for method overriding, including the same method signature and access modifier constraints.
- Using the @Override annotation to prevent mistakes in overriding.
- Differences between method overriding and method overloading.
- Real-world examples, including overriding toString(), equals(), and hashCode().