Covariant Return types in Java


As you know that in the overriding, the return type of subclass method must be the same as the superclass method return type. But this rule is applicable until Java 1.4 version. In Java 1.5, a new covariant return types feature was introduced. 
By using this feature, It is possible to override any method by changing the return type only if the return type of overriding method in the subclass is a subtype of the declared return type of overridden method instead of being exactly the same type. It is known as covariant return types in Java
Key points:
1. The return type of overriding method in the subclass should be either the same as the return type of superclass or subclass.
2. The return type of overriding method in the subclass should not be a parent of the parent method return type.
3. Covariant return types is applicable for only for object types not for primitive types.
Covariant return types in Java

Java Covariant return types Example Programs


Let's see important practical example programs related to this topic.
Program source code 1:
    package covariantReturnType; public class A { public Object m1(){ System.out.println("Hello, this is a superclass"); return null; } } public class B extends A{ @Override public String m1(){ System.out.println("Hello, this is the subclass"); return null; } } public class AB { public static void main(String[] args) { B b=new B(); b.m1(); A a=new B(); a.m1(); } }
    Output: Hello, this is the subclass Hello, this is the subclass
In the above example program, the superclass declares the m1() method of Object return type and B class overrides the m1() method by changing the return type from Object to String. The return type of the overriding method m1() in the subclass is a string that is the subclass of the object class.
1. b.m1() will call the m1() of class B because the reference variable is pointing to the objects of class B. As you learned from the previous tutorial that in overriding, method resolution always takes care by JVM based on the runtime object. 
2. Similarly, a.m1() will also call m1() of class B because it is also pointing to the object of class B.
Program source code 2:
    package covariantReturnType; public class A { public String m1(){ System.out.println("Hello, this is a superclass"); return null; } } public class B extends A{ @Override public Object m1() // Return type of overriding method is incompatible with A.m1() because the return type of overriding method in the subclass should not be parent. Here, Object is the parent class of String. Therefore, it is not overrided method. { System.out.println("Hello, this is the subclass"); return null; } } public class AB { public static void main(String[] args) { A a=new B(); a.m1(); } }
    Output: Hello, this is a superclass
Because of not being overriding in the subclass, a.m1() will call by default available m1() method in class B.
Program source code 3:
    package covariantReturnType; public class A { public Number m1(int a, double b){ System.out.println("Hello, this is a superclass"); return null; } } public class B extends A{ @Override public Integer m1(int a, double b){ System.out.println("Hello, this is the subclass"); return null; } } public class AB { public static void main(String[] args) { A a=new B(); a.m1(10,20.5); } }
    Output: Hello, this is the subclass
Program source code 4:
    package covariantReturnType; public class X { public Object m1(char c){ System.out.println("m1-X"); return new X(); } } public class Y extends X{ @Override public StringBuffer m1(char c){ System.out.println("m1-Y"); return null; } } public class XY { public static void main(String[] args) { X x=new Y(); x.m1('a'); X x1=new X(); x1.m1('b'); } }
    Output: m1-Y m1-X
Program source code 5:
    package covariantReturnType; public class X { public double m1(char c){ System.out.println("m1-X"); return 25; } } public class Y extends X{ @Override public Integer m1(char c) // Error because the return type is incompatible with X.m1(char). Covariant return type concept is applicable only for an object reference type, not for primitive types because there is no subtype relationship between primitive types. { System.out.println("m1-Y"); return 20; } } public class XY { public static void main(String[] args) { X x=new Y(); x.m1('a'); X x1=new X(); x1.m1('b'); } }
    Output: m1-X m1-X
Because of not being overriding in the subclass, x.m1('a') will call by default available m1() method in class Y. Simlarly, x1.m1('b') will call m1 method of class A because reference variable x1 is pointing to the objects of class A.

Now suppose if we declare also double as the return type of the overriding method in the subclass as in the superclass then there will not be a compile-time error. So, in this case, the method will be overriding. The result will be "m1-Y" and "m1-X".

Final words 
Hope that this article has covered all practical example programs related to the covariant return types in Java. We hope that you will have enjoyed this article. All practical example programs are very important for technical test and interview.
Thanks for reading!