Understanding Method Overriding In Java

Understanding Method Overriding In Java
Understanding Method Overriding In Java

Introduction

What would happen if the child class has the same method as declared in the parent class?

When the subclass(or child class) has the same method signature as the parent class(or superclass), that condition is called Method Overriding in Java. In this scenario, the method to be invoked is decided by the Java Virtual Machine based on the runtime object. This process will be discussed in detail in the next section of this blog. 

Method overriding is a feature that allows the child class to provide a specific implementation of the method that has been declared by the parent class. It offers the option to define a specific behavior to the subclass type based on its requirements.  In simple terms, it means to override the functionality of an existing method.

In method overriding in Java, the parent class method is called the overridden method, and the child class method is called the overriding method.

How does Method Overriding in Java Work?

The parent class(or base class) methods are by default available in the child class(or derived class) through inheritance. However, if the child class requires any specific implementation for the method declared in the parent class.

It can redefine the method based on its requirements. The method to be invoked is decided at runtime by the Java Virtual Machine (JVM) based on the runtime object (not on the reference type). 

If a parent class object is used to invoke the method, then the parent class method is executed, but if a child class object is used to invoke the method, then the child class method is executed.

Examples of Method Overriding in Java

Let’s look at the example to understand Method Overriding in Java. Before that, let’s understand the problem that occurs if method overriding is not used.

class Human {
 public void job() {
   System.out.println("Working Professional");
 }
}
 
class Teacher extends Human {
}
 
class Doctor extends Human {
}
 
public class Main {
public static void main(String[] args) {
  Human h1 = new Human();
  Teacher t1 = new Teacher();
  Doctor d1 = new Doctor();
  Human h2 = new Doctor();
  // method invoked based on human object
  h1.job();
  // method invoked based on teacher object
  t1.job();
  // method invoked based on doctor object
  d1.job();
  // method invoked based on doctor object
  h2.job();
 }
}


Output
Working Professional
Working Professional
Working Professional
Working Professional

In the above example, the base class is Human, and the child classes are Teacher and Doctor. When the job() method is called, the parent method is called in every case. In this case, it is not possible to provide a specific implementation of the job() method in the child class. To solve this issue, method overriding is used.

class Human {
 //  overridden method
 public void job() {
   System.out.println("Working Professional");
 }
}
 
class Teacher extends Human {
 //  overriding method
 public void job() {
   System.out.println("Teacher");
 }
}
 
class Doctor extends Human {
 // overriding method
 public void job() {
   System.out.println("Doctor");
 }
}
 
public class Main {
public static void main(String[] args) {
  Human h1 = new Human();
  Teacher t1 = new Teacher();
  Doctor d1 = new Doctor();
  Human h2 = new Doctor();
  // method invoked based on human object
  h1.job();
  // method invoked based on teacher object
  t1.job();
  // method invoked based on doctor object
  d1.job();
  // method invoked based on doctor object
  h2.job();
 }
}

In the above case, the job() method in the child class has a specific implementation.  When the job() method is called, the appropriate method will be called depending on the runtime object. 

Output

Working Professional
Teacher
Doctor
Doctor

Polymorphism and Method Overriding in Java

Polymorphism is a feature by which a single action is performed in multiple ways. Method overriding is a form of polymorphism, as a single method is executed in various ways depending on the runtime object. Since the JVM selects the method at runtime, method overriding in Java is an example of runtime polymorphism

The process in which the call to the overridden method is resolved at runtime is known as dynamic method dispatch or runtime polymorphism. It is called dynamic method dispatch, as the method’s functionality is dynamically decided at runtime by the object. 

It is also called Late binding because binding of method and object is done late after compilation.

Rules for Method Overriding in Java

1.The methods should have the same method signature, and there must be inheritance involved. Until Java5, the same return type was also required for overriding. 

In the earlier versions of Java, it was not possible to override any method by changing the return type. In Java5, a concept called covariant return type was introduced, which allowed method overriding in Java by changing the return type.

Covariant return type allows to narrow down the return type of an overridden method without casting the type or checking the return type. It works only for non-primitive return types.

Some of the possible cases:

Return Type of Parent Class MethodPossible Return Types for Child Class Method
ObjectObject, String, String, StringBuffer, etc
NumberNumber, Integer, Float, Long, etc
StringString

Example:

class Obj {
 // return type object
 public Object f1() {
   return "I am a Object.";
 }
}
 
class Str extends Obj {
 // return type String
  public String f1() {
   return "I am a String.";
 }
}
 
class Main {
public static void main(String[] args) {
  Str strObj = new Str();
  System.out.println(strObj.f1());
}
}

Output:

I am a String.

In the above example, method overriding is performed even though the method f1() has different return types in both classes.

2. A final method cannot be overridden. 

A method is made final when specific implementations are not required.  When a method is declared final, the child class cannot override it. 

Example:

class A {
 // overriden final method
 public final void f1() {
   System.out.println("Class A method");
 }
}
 
class B extends A {
 // overriding non-final method
 public void f1() {
   System.out.println("Class B method");
 }
}
 
class Main {
public static void main(String[] args) {
  A a = new A();
  a.f1();
};
}

Error:

This can be resolved by making the overridden method non-final. However, it is possible to make the overriding method final.

class A {
 // overriden non-final method
 public void f1() {
   System.out.println("Class A method");
 }
}
 
class B extends A {
 // overriding final method
 public final void f1() {
   System.out.println("Class B method");
 }
}
 
class Main {
public static void main(String[] args) {
  A a = new A();
  B b = new B();
  a.f1();
  b.f1();
};
}


Output:

Class A method
Class B method

3. Reducing the scope of access modifiers in the overriding method rather than the overridden method is not possible.

The child class is an instance of the parent class and should have at least the same level of access. For example, a protected overridden method can only have a public or protected overriding method. 

Accessibility Scope:

Private < Default < Protected < Public

Access Modifier of Parent Class MethodPossible Access Modifiers for Child Class Method
PublicPublic
ProtectedProtected, Public
DefaultDefault, Protected, Public

Example:

class A {
 // overriden method with public access modifier
 public void f1() {
   System.out.println("Class A method");
 }
}
 
class B extends A {
 // overriding method with default access modifier
 default void f1() {
   System.out.println("Class B method");
 }
}
 
class Main {
public static void main(String[] args) {
  A a = new A();
  B b = new B();
  a.f1();
  b.f1();
};
}

Error:

This can be resolved by making the overriding method public.

Note: A private method cannot be overridden. Private methods are not visible to any other class, limiting their scope to the child class. They get bonded during the compile-time, so it’s not possible to override private methods in a subclass. 

Example:

class A {
// overriden method with public access modifier
 private void f1() {
  System.out.println("Class A method");
}
}
class B extends A {
 // overriding method with default access modifier
  void f1() {
  System.out.println("Class B method");
}
}
class Main {
public static void main(String[] args) {
 A a = new B();
 a.f1();
}
}

Error:

4. If the child class throws a checked exception, the parent class should throw the same exception or parent exception. Else a compile-time error would occur.

For example, if a child class throws ArithmeticException, the parent class can only throw the parent exceptions of ArithmeticException, RunTimeException, or Exception and not any other exceptions like IOException. This is because the compiler cannot catch the exception in this case. 

Since unchecked exceptions can be thrown anywhere, this rule is not applicable to them.

Refer to the image below, to understand the hierarchy of Exceptions in Java.

Image Source: Hierarchy of Exceptions in Java

Example:

import java.io.IOException;
 
class A {
// overriden method with IOException
 public void f1() throws IOException {
    throw new IOException();
}
}
class B extends A {
 // overriding method with Exception
   public void f1() throws Exception {
  try {
      System.out.println("Class B method");
    } catch(Exception e) {
      System.out.println(e);
    }
}
}
class Main {
public static void main(String[] args) {
 B b = new B();
 try {
   b.f1();
 } catch(Exception e) {
   System.out.println(e);
 }
}
}

Error:

Compile-time error is caused in the above case because child class A throws a checked exception(Exception), but the parent class is throwing a child exception(IOException) instead of a parent exception.

The overriding method can only throw those checked exceptions, which have less scope than the exception declared in the overridden method.

import java.io.IOException;
 
class A {
// overriden method with IOException
 public void f1() throws Exception {
  try {
      System.out.println("Class B method");
    } catch(Exception e) {
      System.out.println(e);
    }
}
}
class B extends A {
 // overriding method with Exception
  public void f1() throws IOException {
    throw new IOException();
}
}
class Main {
public static void main(String[] args) {
 B b = new B();
 try {
   b.f1();
 } catch(Exception e) {
   System.out.println(e);
 }
}
}

Output:

java.io.IOException

This does not show any error as the child class throws a checked exception and the parent class throws the corresponding parent exception.

5. A static method cannot be overridden.

When child class and parent class are static with the same signature, it is called method hiding. The method in the parent class will be hidden by the one that is in the child class. In method hiding, method resolution is taken care of by the compiler based on the reference type.

In method overriding, the method to be invoked is decided at runtime. Static methods are bonded at compile-time, so static methods cannot be overridden in Java but static keywords do.

Example:

class A {
// overriden static method
 static void f1() {
    System.out.println("Class A method");
}
}
class B extends A {
 // overriding static method
  static void f1() {
    System.out.println("Class B method");
 }
}
class Main {
public static void main(String[] args) {
 A a = new A();
 B b = new B();
 a.f1();
 b.f1();
}
}

Output:

Class A method
Class B method

This does not generate any error. However, this is not method overriding. 

6. A constructor cannot be overridden as a parent class, and a child class cannot have the constructors with the same name. The constructor name should always be the same as the class name.

7. A non-abstract method in the child class must override the abstract method declared in the parent class, vice-versa is not possible.

An abstract class cannot be used to create objects, and the implementation has to be provided by the sub-class. So it should have a non-abstract class to achieve method overloading.

Example:

abstract class A {
// overriden abstract method
 abstract void f1();
}
class B extends A {
 // overriding non-abstract method
  void f1() {
    System.out.println("Class B method");
  }
}
class Main {
public static void main(String[] args) {
 B b = new B();
 b.f1();
}
}

Output:

Class B method

8. A synchronised/strictfp/native method can correspondingly override a non-synchronised/non-strictfp/non-native method and vice-versa.

9. A child class in a different package can only override the non-final methods declared public or protected.

10. A child class within the same package as the instance’s parent class can override any parent class method that is not declared final or private.

super() in Method Overriding in Java

super() can be used to invoke a parent class overridden method in the child class method.

Example:

class A {
 // overridden method
 public void f1() {
   System.out.println("Overridden method");
 }
}
class B extends A {
  // overriding method
  public void f1() {
   // overridden parent method called
   super.f1();
   System.out.println("Overriding method");
  }
}
class Main {
public static void main(String[] args) {
 B b = new B();
 b.f1();
}
}


Output:

Overridden method
Overriding method

In the above example, both the overridden and overriding methods are executed with the help of super().

A real-world example of Method Overloading in Java

Consider a scenario where a school is taking admissions for the 11th standard, and a method has to be written to display the fee. The fee would vary according to the stream selected by the student, so method overriding can be used to get the fee of each stream.

class XIAddmission {
 // overriden method
 public int getFee() {
   int baseFee = 10000;
   return baseFee;
 }
}
class Science extends XIAddmission {
  // overriding method
 public int getFee() {
   int extraFee = 5000;
   return super.getFee() + extraFee;
 }
}
 
class Commerce extends XIAddmission {
  // overriding method
 public int getFee() {
   int extraFee = 2500;
   return super.getFee() + extraFee;
 }
}
 
class Humanities extends XIAddmission {
  // overriding method
 public int getFee() {
   int extraFee = 2000;
   return super.getFee() + extraFee;
 }
}
class Main {
public static void main(String[] args) {
 Science s1 = new Science();
 Commerce s2 = new Commerce();
 Humanities s3 = new Humanities();
 System.out.println("Fee for Science stream: Rs."+ s1.getFee());
 System.out.println("Fee for Commerce stream: Rs."+ s2.getFee());
 System.out.println("Fee for Humanities stream: Rs."+ s3.getFee());
}
}

Output:

Fee for Science stream: Rs.15000
Fee for Commerce stream: Rs.12500
Fee for Humanities stream: Rs.12000

In the above example, XIAddmission class has a method getFee() to return the base fee. Each stream class has an overriding getFee() method, which includes the extra fee in the final fee.

Frequently Asked Questions

What is the difference between overriding and overloading in Java?

When a class has multiple methods with the same name but different parameters, it is called method overloading in Java. When the child class has the same method signature as the parent class, that condition is called Method Overriding in Java.

Why is method overriding used?

It is used to provide the specific implementation of the method already provided by its parent class.

Is it possible to override constructors in Java?

No, as a parent class, and a child class cannot have the constructors with the same name

Is it possible to override the main method in Java?

No, the main method is a static method, and a static method cannot be overridden.

Is method overriding performed within a class?

No, it performed in more than one class that has an inheritance relationship.

Key Takeaways

Want to test your understanding of method overriding? Check out this coding problem on method overriding.

This blog attempted to explain method overriding in Java in detail, along with the rules and various examples.

We hope you found this blog useful. Feel free to let us know your thoughts in the comments section. 

By Hari Sapna Nair