Table of Contents

    Covariant Return Type in Java: Definition and Examples

    Covariant Return Type in Java: Definition and Examples

    Before JDK 5.0, it was not possible to override a method by changing the return type. When we override a parent class method, the name, argument types and return type of the overriding method in child class has to be exactly same as that of parent class method. Overriding method was said to be invariant with respect to return type.
    it is possible to override method by changing the return type if subclass overrides any method whose return type is Non-Primitive but it changes its return type to subclass type.

    Covariant return types

    Java 5.0 onwards it is possible to have different return type for a overriding method in child class, but child's return type should be sub-type of parent's return type. Overriding method becomes variant with respect to return type.

    Co-variant return type is based on Liskov substitution principle.

    Let's take a simple example to understand the co-variant return type with method overriding.

    Simple example of Covariant Return Type

    Program:
    class Vechile{
    	Vechile get(){
    		return this;
    		}
    }
    
    class Car extends Vechile{
    	Car get(){
    		return this;
    		}
    void message(){
    	System.out.println("covariant return type");
    	}
    
    public static void main(String args[]){
    	new Car().get().message();
    	}
    }
    Output:
    covariant return type
    Press any key to continue . . .

    As you can see in the above example, the return type of the get() method of Vechile class is Vechile but the return type of the get() method of Car class is Car. Both methods have different return type but it is method overriding. This is known as covariant return type.

    Another important example of Covariant Return Type

    Program:
    // Java program to demonstrate that we can have
    // different return types if return type in
    // overridden method is sub-type
    
    // Two classes used for return types.
    class ClassA {}
    class ClassB extends ClassA {}
    
    class Base
    {
        ClassA fun()
        {
            System.out.println("Inside Base fun()");
            return new ClassA();
        }
    }
    
    class Derived extends Base
    {
        ClassB fun()
        {
            System.out.println("Inside Derived fun()");
            return new ClassB();
        }
    }
    
    public class Main
    {
        public static void main(String args[])
        {
           Base base = new Base();
           base.fun();
    
           Derived derived = new Derived();
           derived.fun();
        }
    }
    Output:
    Inside Base fun()
    Inside Derived fun()
    Press any key to continue . . .

    Note : If we swap return types of Base and Derived, then above program would not work.

    Advantages:

    • It helps to avoid confusing type casts present in the class hierarchy and thus making the code readable, usable and maintainable.
    • We get a liberty to have more specific return types when overriding methods.
    • Help in preventing run-time ClassCastExceptions on returns

    Real Life Implementation

    Program:
    class WildAnimal {
    	public String willYouBite(){
    		return "Yes";
    	}
    }
    
    class Lion extends WildAnimal {
    	public String whoAreYou() {
    		return "Lion";
    	}
    }
    
    class BengalTiger extends WildAnimal {
    	public String whoAreYou() {
    		return "Bengal Tiger";
    	}
    }
    
    class Zoo {
         WildAnimal getWildAnimal() {
             return new WildAnimal();
         }
     }
    
    class AfricaZoo extends Zoo {
         @Override
         Lion getWildAnimal() {
             return new Lion();
         }
    }
    
    class IndiaZoo extends Zoo {
         @Override
         BengalTiger getWildAnimal() {
             return new BengalTiger();
         }
    }
    
    public class CovariantClass {
    	public static void main(String args[]){
    		AfricaZoo afZoo = new AfricaZoo();
    		System.out.println(afZoo.getWildAnimal().whoAreYou());
    		IndiaZoo inZoo = new IndiaZoo();
    		System.out.println(inZoo.getWildAnimal().whoAreYou());
    	}
    }
    Output:
    Lion
    Bengal Tiger
    Press any key to continue . . .

    In class Zoo, the method getWildAnimal returns ‘WildAnimal’ which is a super type. AfricaZoo extends Zoo and overrides the method getWildAnimal. While overriding, the return type of this method is changed from WildAnimal to Lion. This demonstrates covariant type / Liskov substitution principle. We are replacing the supertype’s (WildAnimal) instance with subtype’s (Lion) instance. This was not possible before JDK 1.5 IndiaZoo is just another example which demonstrates the same covariant type.