Java utilizes Method Overriding to enable dynamic behavior and runtime polymorphism. This section will cover the concept of method overloading, guidelines for its implementation, and provide illustrative examples to enhance understanding.
What is Method Overriding in Java?
Method Overriding enables a subclass to define its own unique implementation of a method that is already declared in its superclass. This feature facilitates dynamic method binding during runtime, allowing the method invocation to be determined at runtime according to the specific object type.
Usage of Java Method Overriding
The following points describe the uses of method overriding in Java:
- Method overriding is used to provide the specific implementation of a method that is already provided by its superclass.
- Method overriding is used for runtime polymorphism.
- Method overriding allows subclasses to reuse and build upon the functionality provided by their superclass, reducing redundancy and promoting modular code design.
- Subclasses can override methods to tailor them to their specific needs or to implement specialized behavior that is unique to the subclass.
- Method overriding enables dynamic method dispatch, where the actual method implementation to be executed is determined at runtime based on the type of object, supporting flexibility and polymorphic behavior.
Rules for Java Method Overriding
The following are the important rules of method overriding in Java:
- Same Method Name: The overriding method in the subclass must have the same name as the method in the superclass that it is overriding.
- Same Parameters: The overriding method must have the same number and types of parameters as the method in the superclass. This ensures compatibility and consistency with the method signature defined in the superclass.
- IS-A Relationship (Inheritance): Method overriding requires an IS-A relationship between the subclass and the superclass. This means that the subclass must inherit from the superclass, either directly or indirectly, to override its methods.
- Same Return Type or Covariant Return Type: The return type of the overriding method can be the same as the return type of the overridden method in the superclass, or it can be a subtype of the return type in the superclass. This is known as the covariant return type, introduced in Java 5.
- Access Modifier Restrictions: The access modifier of the overriding method must be the same as or less restrictive than the access modifier of the overridden method in the superclass. Specifically, a method declared as public in the superclass can be overridden as public or protected but not as private. Similarly, a method declared as protected in the superclass can be overridden as protected or public but not as private. A method declared as default (package-private) in the superclass can be overridden with default, protected, or public, but not as private.
- No Final Methods: Methods declared as final in the superclass cannot be overridden in the subclass. Because final methods cannot be modified or extended.
- No Static Methods: Static methods in Java are resolved at compile time and cannot be overridden. Instead, they are hidden in the subclass if a method with the same signature is defined in the subclass.
Understanding the problem without method overriding
Let's explore the challenges that may arise in a program when method overriding is not utilized.
Example
//Java Program to demonstrate why we need method overriding
//Here, we are calling the method of parent class with child class object
//Creating a parent class
class Vehicle{
void run(){System.out.println("Vehicle is running");}
}
//Creating a child class
class Bike extends Vehicle{}
//Creating a Main class to create object and call methods
public class Main{
public static void main(String args[]){
//creating an instance of child class
Bike obj = new Bike();
//calling the method with child class instance
obj.run();
}
}
Output:
Vehicle is running
Explanation
An object of the Bike class is created and its run method is called. Since Bike inherits from Vehicle, the run method in the Bike class overrides the one in the Vehicle class. Consequently, when the run method is invoked on a Bike object, it displays "Vehicle is running". This showcases how method overriding, where a method in the subclass replaces the one in the superclass with the same signature, leads to polymorphic behavior.
Example of Method Overriding
In this instance, we have specified the run function in the derived class Bike, which also exists in the base class Vehicle, albeit with a distinct implementation. Both methods share identical names and parameters, establishing an IS-A connection between the classes, thereby resulting in method overriding.
Example
//Java Program to illustrate the use of Java Method Overriding
//Creating a parent class.
class Vehicle{
//defining a method
void run(){System.out.println("Vehicle is running");}
}
//Creating a child class
class Bike extends Vehicle{
//defining the same method as in the parent class
void run(){System.out.println("Bike is running safely");}
}
//Creating a Main class to create object and call method
public class Main{
public static void main(String args[]){
Bike obj = new Bike();//creating object
obj.run();//calling method
}
}
Output:
Bike is running safely
Explanation
In the context of a Java program, there is an example where a method in a subclass called Bike overrides a method with the same name in its superclass, which is named Vehicle. This demonstrates the concept of method overriding in Java. By having the run method present in both classes, the subclass, Bike, provides its own implementation, resulting in the output "Bike is running safely." When an object of the Bike class is created and the run method is invoked, the overridden method in the Bike class is executed. This showcases how the subclass's implementation takes precedence over the superclass's implementation. The scenario highlights Java's dynamic polymorphism, which enables methods with identical signatures to exhibit different behaviors across classes, even within the same inheritance hierarchy.
A Real World Example of Java Method Overriding
Imagine a situation where there is a class called Bank that offers a feature to retrieve the interest rate. Nonetheless, the interest rate differs based on the bank. For instance, SBI, ICICI, and AXIS banks might offer interest rates of 8%, 7%, and 9% respectively.
Example
//Java Program to demonstrate the real scenario of Java Method Overriding
//where three classes are overriding the method of a parent class.
//Creating a parent class.
class Bank{
int getRateOfInterest(){return 0;}
}
//Creating child classes
class SBI extends Bank{
int getRateOfInterest(){return 8;}
}
class ICICI extends Bank{
int getRateOfInterest(){return 7;}
}
class AXIS extends Bank{
int getRateOfInterest(){return 9;}
}
//Create a Main class to create objects and call the methods
public class Main{
public static void main(String args[]){
SBI s=new SBI();
ICICI i=new ICICI();
AXIS a=new AXIS();
System.out.println("SBI Rate of Interest: "+s.getRateOfInterest());
System.out.println("ICICI Rate of Interest: "+i.getRateOfInterest());
System.out.println("AXIS Rate of Interest: "+a.getRateOfInterest());
}
}
Output:
SBI Rate of Interest: 8
ICICI Rate of Interest: 7
AXIS Rate of Interest: 9
Explanation
In this Java program, a practical scenario is simulated where three classes - SBI, ICICI, and AXIS - override a method inherited from their parent class, Bank. This demonstration showcases the concept of method overriding. Initially, the Bank class's getRateOfInterest function returns a value of 0. Subsequently, each of the child classes provides its own implementation by overriding this method: SBI returns 8, ICICI returns 7, and AXIS returns 9.
Within the Test2 class, objects of each child class are instantiated. By invoking the getRateOfInterest method for each object, the respective interest rates for each bank are displayed. This example effectively highlights the polymorphic behavior achieved through method overriding. It emphasizes how each subclass can offer a unique implementation of a method inherited from the superclass, allowing for dynamic behavior based on the object's type at runtime.
Static Methods and Method Overriding
In Java, it is not possible to override static methods. If a subclass defines a static method with an identical signature as a static method in its superclass, it does not override the method but rather conceals the superclass method. Consequently, the method that is executed is chosen at compile time according to the reference type and not at runtime based on the real object type. Consequently, static methods do not facilitate runtime polymorphism as instance methods do.
Method overriding relies on dynamic method dispatch, meaning that the method call is determined at runtime based on the specific object. On the other hand, static methods are linked to the class rather than instances, so they are resolved during compile time. Consequently, static methods do not support dynamic dispatch and are not subject to overriding.
Moreover, there is a distinction in the memory management of static and instance methods. Instance methods are accessed via objects and are associated with heap memory, whereas static methods are kept in the method area of the JVM and are accessible to all instances of the class. Because of these core variances in functionality, linking, and memory assignment, static methods are ineligible for method overriding.
Method Overriding and main Method
The main function cannot be overridden due to its static declaration. It is specified as public static void main(String args) and acts as the starting point for program execution.
Static methods are associated with the class itself rather than instances, thus they do not enable runtime polymorphism. Their resolution occurs during compilation, preventing method overriding. Consequently, even if a subclass declares a main method with an identical signature, it does not replace the superclass method, rendering the main method non-overridable in Java.
Method Overloading Vs. Method Overriding
Below is a table illustrating the distinction between Method Overloading and Method Overriding:
| Aspect | Method Overloading | Method Overriding |
|---|---|---|
| Purpose and Intent | Method overloading is used to increase the readability of the program. | Method overriding is used to provide the specific implementation of the method that is already provided by its superclass. |
| Relationship between Classes | Method overloading is performed within class. | Method overriding occurs in two classes that have IS-A (inheritance) relationship. |
| Parameter Requirements | In case of method overloading, parameters must be different. | In case of method overriding, parameters must be the same. |
| Polymorphism Type | Method overloading is the example of compile-time polymorphism. | Method overriding is the example of runtime polymorphism. |
| Return Type Constraints | In Java, method overloading can't be performed by changing the return type of the method only. Return type can be the same or different in method overloading, but you must have to change the parameter. | Return type must be the same or covariant in method overriding. |
| Access Modifiers | It can have any access modifier. | The overriding method cannot have a more restrictive access modifier than the overridden method. |
| Exceptions | It can throw any exceptions. | It throws only checked exceptions declared in the superclass method or its subclasses. |
| Invocation | It resolved at compile-time. | It resolved at runtime using dynamic method dispatch. |
| Inheritance Required | Not required. | Required (through subclassing or interface implementation). |
| Binding | It uses static binding. | It uses dynamic binding. |
| Early and Late Binding | It is also known as early binding. | It is also known as late binding. |
| Signature Matching | Method signatures must be different. | Method signatures must be identical. |
| Statis Method | Static method can be overloaded. | Static method cannot be overridden. |
| Final and Public Method | It is applicable to both private and final methods. | It is not applicable to private and final methods. |
| Error | If any error occurs, can be caught at compile-time. | If any error occurs, can be caught at run-time. |
Java Access Modifiers with Method Overriding
When you are overriding a method, it is important to ensure that the overridden method in the subclass is not made more restrictive.
Example
To illustrate the concept of Access Modifiers in Java when used in Method Overriding, let's consider an example.
class A{
protected void msg(){System.out.println("Hello java");}
}
public class Simple extends A{
void msg(){System.out.println("Hello java");}//C.T.Error
public static void main(String args[]){
Simple obj=new Simple();
obj.msg();
}
}
Output:
Explanation
In the provided Java code, Class A has a method named msg declared with the protected access modifier. The class Simple attempts to override the msg method with a default access modifier upon extending Class A. However, a compile-time error occurs because the default access modifier is more restrictive than protected. In Java, when overriding a method, the subclass's access level for the overridden method must be as permissive as or more permissive than the superclass's method. To resolve this issue, either the access modifier of the method in class Simple, like protected or public, should be as permissive as protected, or the method in Class A should have a less restrictive access modifier, such as default or public.