Object-Oriented Programming (OOP) is a core part of Java. Among the many concepts in OOP, Method Overriding and the usage of the final
and super
keywords play crucial roles in writing clean, maintainable, and extensible code. In this blog post, we’ll break down these concepts and explore how they fit together.
1. Method Overriding
Method overriding allows a subclass to provide its specific implementation for a method already defined in its superclass. This is an essential feature of Java’s inheritance model, as it enables polymorphism, where a subclass can have different behaviors for the same method signature defined in the parent class.
How Does Method Overriding Work?
To override a method in Java, the method in the subclass must have the same name, return type, and parameters as in the superclass. The subclass version of the method is called when an object of the subclass is used, even if the reference is of the superclass type.
Example:
class Animal {
void sound() {
System.out.println("Animal makes a sound");
}
}
class Dog extends Animal {
@Override
void sound() {
System.out.println("Dog barks");
}
}
public class Main {
public static void main(String[] args) {
Animal obj = new Dog(); // Reference of Animal type but Dog object
obj.sound(); // Output: Dog barks
}
}
In this example, the Dog
class overrides the sound()
method from the Animal
class. Even though the reference type is Animal
, the Dog
class’s method is invoked.
Rules for Method Overriding:
- The method must have the same name and parameters as in the superclass.
- The access level cannot be more restrictive than the overridden method’s access level. For example, if the superclass method is
public
, the subclass method cannot beprivate
. - The overriding method cannot throw more checked exceptions than the method it overrides.
2. The final
Keyword in Java
The final
keyword can be applied to variables, methods, and classes. It is used to impose restrictions on code functionality, ensuring that certain aspects cannot be changed.
Usage of final
with Methods:
When a method is marked as final
, it cannot be overridden by any subclass. This is particularly useful when you want to prevent altering the behavior of a method in derived classes.
Example:
class Vehicle {
final void run() {
System.out.println("Vehicle is running");
}
}
class Bike extends Vehicle {
// This would cause a compile-time error
// void run() {
// System.out.println("Bike is running");
// }
}
In this case, the run()
method in the Vehicle
class is marked final
, preventing any subclass (like Bike
) from overriding it.
Usage of final
with Classes:
Marking a class as final
means that it cannot be subclassed. This is often used for security reasons or when the design of the class is such that it should not be extended.
Example:
final class Constants {
// Constant values
}
Once marked as final
, no other class can extend the Constants
class.
Usage of final
with Variables:
When a variable is declared final
, its value cannot be modified once assigned. This is useful for defining constants or ensuring that a reference remains unchanged.
Example:
final int MAX_SPEED = 120;
3. The super
Keyword in Java
The super
keyword in Java is used to refer to the immediate parent class object. It is commonly used in two situations:
- To access the parent class's method that has been overridden in the child class.
- To call the parent class’s constructor.
Using super
to Call Parent Class Methods:
If a method is overridden, but you still need to access the parent class's version of that method, you can use the super
keyword.
Example:
class Animal {
void sound() {
System.out.println("Animal makes sound");
}
}
class Dog extends Animal {
@Override
void sound() {
super.sound(); // Calling parent class's method
System.out.println("Dog barks");
}
}
In this example, the Dog
class overrides the sound()
method but still calls the parent class (Animal
) method using super
.
Using super
to Call Parent Class Constructor:
The super
keyword can also be used to explicitly call a parent class constructor from a subclass. If not explicitly called, the compiler automatically inserts a call to the no-argument constructor of the parent class.
Example:
class Person {
Person(String name) {
System.out.println("Person's name: " + name);
}
}
class Employee extends Person {
Employee(String name, int id) {
super(name); // Calling parent class constructor
System.out.println("Employee ID: " + id);
}
}
public class Main {
public static void main(String[] args) {
Employee emp = new Employee("John", 101);
}
}
public class Main {
public static void main(String[] args) {
Dog obj = new Dog();
obj.sound(); // Output: Animal makes sound, Dog barks
}
}
In this example, the constructor of the Employee
class uses super(name)
to call the constructor of the Person
class.