Understanding Method Overriding in Java
In this article, we will explore a fundamental concept of object-oriented programming in Java: method overriding. To fully understand this, we first need to grasp the concept of inheritance, as method overriding is inherently linked to it.
Inheritance in Java
Inheritance is one of the pillars of object-oriented programming. In Java, inheritance allows a class to inherit the properties (attributes) and behaviors (methods) of another class. The class that inherits is called the child class (or subclass), and the class from which it inherits is called the parent class (or superclass).
Example of Inheritance
class Animal {
void makeSound() {
System.out.println("The animal makes a sound");
}
}
class Dog extends Animal {
void makeSound() {
System.out.println("The dog barks");
}
}
In this example, Dog
inherits from Animal
. The Dog
class can use the methods of the Animal
class, but it can also provide its own implementation for these methods.
Method Overriding
Method overriding occurs when a child class provides a specific implementation for a method that is already defined in its parent class. Overriding allows the child class to customize or extend the behavior of the inherited method.
Rules of Method Overriding
Same Signature: The overridden method must have the same name, number and types of parameters, and the same order of parameters.
Return Type: The return type must be the same or a covariant subtype of the return type in the parent class.
Access Modifiers: The overridden method cannot have a more restrictive access modifier than the method in the parent class.
Exceptions: The overridden method cannot throw broader or more general exceptions than those declared in the parent class method.
Examples of Method Overriding
Example with Covariant Return Type
class Parent {
public Number method() {
return 42;
}
}
class Child extends Parent {
@Override
public Integer method() {
return 42;
}
}
In the example above, the return type Integer
in the Child
class is a covariant subtype of Number
, allowing the method to be overridden.
Invalid Overriding Example
class Parent {
public Number method() {
return 42;
}
}
class Child extends Parent {
@Override
public String method() {
return "42";
}
}
Here, attempting to override method
with a return type of String
results in a compilation error because String
is not a covariant subtype of Number
.
Overriding and Exceptions
In method overriding, the exceptions thrown by the overridden method must be compatible with the exceptions thrown by the method in the parent class. The overridden method can throw fewer exceptions or exceptions that are subtypes of the declared exceptions.
Example with Exceptions
class Parent {
public void method() throws IOException {
// implementation
}
}
class Child extends Parent {
@Override
public void method() throws FileNotFoundException {
// implementation
}
}
In the example above, FileNotFoundException
is a subtype of IOException
, allowing the method to be overridden.
Invalid Example with Exceptions
class Parent {
public void method() throws IOException {
// implementation
}
}
class Child extends Parent {
@Override
public void method() throws Exception {
// implementation
}
}
In this case, attempting to override method
with a broader exception Exception
results in a compilation error because Exception
is not a subtype of IOException
.
Complex Example of Inheritance and Method Overriding
To better illustrate method overriding, let's consider a more complex example with inheritance and overriding three methods.
class Vehicle {
public void start() {
System.out.println("Vehicle started");
}
public void accelerate() {
System.out.println("Vehicle accelerating");
}
public Number getSpeed() {
return 60;
}
}
class Car extends Vehicle {
@Override
public void start() {
System.out.println("Car started");
}
@Override
public void accelerate() {
System.out.println("Car accelerating");
}
@Override
public Integer getSpeed() {
return 120;
}
}
public class TestVehicle {
public static void main(String[] args) {
Vehicle myVehicle = new Vehicle();
myVehicle.start(); // Output: Vehicle started
myVehicle.accelerate(); // Output: Vehicle accelerating
System.out.println("Speed: " + myVehicle.getSpeed()); // Output: Speed: 60
Vehicle myCar = new Car();
myCar.start(); // Output: Car started
myCar.accelerate(); // Output: Car accelerating
System.out.println("Speed: " + myCar.getSpeed()); // Output: Speed: 120
}
}
In this example, the Car
class extends the Vehicle
class and overrides three methods: start
, accelerate
, and getSpeed
. In the main
method, we can see how method overriding allows the specific behavior of Car
to be executed even when the object is treated as a Vehicle
.
Conclusion
Method overriding is a powerful tool in object-oriented programming, allowing child classes to customize and extend the behavior of parent classes. Understanding the rules and limitations of method overriding is crucial for writing robust and efficient Java code. By following the guidelines on return types, access modifiers, and exceptions, you can make the most of this feature to create well-structured and flexible applications.
I hope this article has helped clarify how method overriding works in Java. If you have any questions or comments, feel free to share them below.
Subscribe to my newsletter
Read articles from André Felipe Costa Bento directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
André Felipe Costa Bento
André Felipe Costa Bento
Fullstack Software Engineer.