Java Overriding: Widening and narrowing for access modifier, return type and exception handling

In this article, we will understand Java overriding concepts with respect to Java method signature i.e.

  • Access modifier
  • Return type (also, will look into co-variant return type)
  • Exception handling

Java overriding

While inheriting super class, if a sub class has a same instance method with same method signature as that of the super class’s method then it is said to be method overriding in Java.

Same method signature means, exactly same

  • Method name
  • Number of formal parameters (input parameters)
  • Access modifier (or more wider/broader access modifier of overridden method)
  • Return type (sub class or sub-type of overridden method’s return type)
  • Checked exception (sub class or sub-type of overridden method’s exception)

Note: There is no restriction on un-checked exception, as overriding method in the sub class can throw any un-checked exceptions irrespective of the overridden method’s exception in throws clause

Reason to have another exactly same method in the inheriting class is to provide more specific implementation from that of more general implementation in super class

Java method signature:

  • Instance method (non-static)
  • Static method (class method)

We will concentrate only on instance method signature and list out limitations or rules w.r.t access modifier, return type and exception handling while overriding

Instance method (non-static):

Java method signature with access modifier, return type and throws clause (exception)

public int add(int number1, int number2) throws Exception {
		
	return summation;
}

 

Diagrammatic representation of Instance method

1_Method_Overriding_Instance_Method_Signature

Example for Instance method

Super class>> Shape (in.bench.resources.method.overriding)
Overridden method>> draw()

access modifier: public
return type: void
throws: Exception (java.lang.Exception)

2_Method_Overriding_Instance_method_in_super_class

Sub class>> Circle (in.bench.resources.method.overriding)
Extending Super class>> Shape (in.bench.resources.method.overriding)
Overriding method>> draw()

access modifier: public
return type: void
throws: Exception (java.lang.Exception)

3_Method_Overriding_Instance_method_in_sub_class

In above example, method signature in the overriding method in sub class is exactly same as that of super class’s overridden method

But we can narrow/widen few things in Java method signature w.r.t below three things

  • Access modifier
  • Return type
  • Exception

 

Let us understand widening & narrowing concept

Access modifier:

There are four types of access modifier in Java for defining access visibility (or to define scope) of

  • member variable
  • method
  • constructor
  • class

These four access modifiers are tabulated below,

 
Access modifier
 
Visibility/Accessibility
private visible/accessible only within class
default visible/accessible only inside the same package
protected visible/accessible within the package and all sub classes
public visible/accessible everywhere

We will restrict the discussion to Java methods only;

While inheriting method from super class to a sub class, access level can be kept same or it should be wider/broader

Access level increases in below order (with private being the least and public being the highest)

  • private
  • default
  • protected
  • public

Example for widening access level,

Super class>> Shape (in.bench.resources.method.overriding)
Overridden method>> draw()

access modifier: protected
return type: void
throws: Exception (java.lang.Exception)

4_Method_Overriding_Instance_method_for_access_modifier_in_super_class

Sub class>> Circle (in.bench.resources.method.overriding)
Extending Super class>> Shape (in.bench.resources.method.overriding)
Overriding method>> draw()

access modifier: public
return type: void
throws: Exception (java.lang.Exception)

5_Method_Overriding_Instance_method_for_access_modifier_in_sub_class

In above example, access level in the draw() method of super class is ‘protected’. While overriding, access level has been broadened to ‘public’

So, access level in overriding concepts cannot be more restrictive rather it should be more broader/wider

 

Return type:

While overriding, return type should be exactly same or its sub classes (sub type)

Example for narrowing return type,

We will create dummy POJOs to illustrate this example within ‘in.bench.resources.pojo’ package

POJO 1: SuperClassA

6_Method_Overriding_Instance_method_for_return_type_super_class_pojo

POJO 2: SubClassB extending SuperClassA

7_Method_Overriding_Instance_method_for_return_type_sub_class_pojo

Super class>> Shape (in.bench.resources.method.overriding)
Overridden method>> draw()

access modifier: default (no explicit access modifier)
return type: SuperClassA (in.bench.resources.pojo)
throws: Exception (java.lang.Exception)

8_Method_Overriding_Instance_method_for_return_type_in_super_class

Sub class>> Circle (in.bench.resources.method.overriding)
Extending Super class>> Shape (in.bench.resources.method.overriding)
Overriding method>> draw()

access modifier: protected
return type: SubClassB (in.bench.resources.pojo)
throws: Exception (java.lang.Exception)

9_Method_Overriding_Instance_method_for_return_type_in_sub_class

In above example, return type in the draw() method of super class is ‘SuperClassA’ and while overriding return type has been changed to ‘SubClassB’ which is a inheriting class from SuperClassA

So, return type in the overriding method can be same or its sub classes (or sub type from that of super class)

This is called co-variant return type in Java (Since JDK 1.5)

 

Exception Handling:

While overriding,

  • throws clause should throw exactly same exception or its sub class’s exception (for checked exception)
  • sub class compiles even if we don’t add throws clause in the overriding method in sub class –> when super class throws checked exception
  • sub class can throw any number of un-checked exception (eg; RuntimeException) –> class compiles without any error/warning

Example for narrowing exception

Super class>> Shape (in.bench.resources.method.overriding)
Overridden method>> draw()

access modifier: default
return type: SuperClassA (in.bench.resources.pojo)
throws: Exception (java.lang.Exception)

10_Method_Overriding_Instance_method_for_exception_handling_in_super_class

Sub class>> Circle (in.bench.resources.method.overriding)
Extending Super class>> Shape (in.bench.resources.method.overriding)
Overriding method>> draw()

access modifier: protected
return type: SubClassB (in.bench.resources.pojo)
throws: ClassNotFoundException (java.lang.ClassNotFoundException)

11_Method_Overriding_Instance_method_for_exception_handling_in_sub_class

In above example, super class’s overridden draw() method throws ‘java.lang.Exception’ whereas overriding method throws ‘java.lang.ClassNotFound.Exception’ (which is sub-class of java.lang.Exception)

In addition to points discussed above for overriding a method in Java, there are few rules to abide while overriding method in sub class

Rules for Java method overriding:

  • To override a method, first class has be to be inherited (or to be sub classed)
  • Overriding method name and signature in the sub class should be exactly same as that of super class’s method
  • Number of formal parameters and their order should be exactly same as that of the overridden method
  • Access level cannot be more restrictive, rather it should be more wider/broader (widening concept)
  • Return type should be same or its sub-type (narrowing concept). This is knowns as co-variant return type (applicable since Java 1.5)
  • Exception in the throws clause should be same or its sub-type (narrowing concept)
  • It is okay, not to throw any checked exception in the overriding method even when super class’s overridden method throws any checked exception but vice-versa results in compilation error
  • Any number of un-checked exception can be thrown from overriding method regardless of whether overridden method throws any checked or un-checked exception in super class
  • If super class method declared as final then it cannot be overridden
  • If super class method declared as private then it cannot be overridden (Singleton design pattern)
  • If super class method declared as static then it cannot be overridden rather it can be re-declared in inheriting class
  • Constructor cannot be overridden

 

Read also:

 

References:

https://docs.oracle.com/javase/tutorial/java/IandI/override.html

Happy Coding !!
Happy Learning !!