• Java |
    Tuesday, December 7, 2004

Covariant return types in Java

Java 1.5 supports covariant return types. What does this mean? Before 1.5, when you override a superclass method, the name, argument types and return type of the overrding method has to be exactly same as that of superclass method. Overriding method is said to be invariant with respect to argument types and return type.

If you change any argument type, then you are not really overriding a method -- you are actually overloading it.

    class Shape {
public void draw(Graphics g, int x, int y) {}
class Circle extends Shape {
public void draw(Graphics g, float x, float y) {}
// Circle.draw overloads the inheried Shape.draw
// -- argument types for 'x' and 'y' are different here.

Also, before 1.5, overriding method can't have different return type. This is relaxed in 1.5. The subclass method's return type R2 may be different from superclass method's return type R1, but R2 should be a subtype of R1. i.e., subclass can return type may be a subtype of superclass return type.

   class ShapeFactory {
public Shape newShape() {}
class CircleFactory extends ShapeFactory {
public Circle newShape() {}

In the above example, CircleFactory.newShape returns Circle type -- which is subtype of superclass method's (ShapeFactory.newShape) return type. With 1.5, this is allowed. Method overriding is said to be covariant with respect to return type. Another example is clone method. Object.clone method returns Object type. A subclass such as Shape's clone method can return Shape type instead of Object type. Interestingly, exception declaration is already covariant even before 1.5. i.e., subclass method may throw either same types or subtypes of superclass method's exceptions.

How is this implemented?

Although the return type based overloading is not allowed by java language, JVM always allowed return type based overloading. JVM uses full signature of a method for lookup/resolution. Full signature includes return type in addition to argument types. i.e., a class can have two or more methods differing only by return type. javac uses this fact to implement covariant return types. In the above, CircleFactory example, javac generates code which is equivalent to the following:

class CircleFactory extends ShapeFactory {
public Circle newShape() {
// your code from the source file
return new Circle();
// javac generated method in the .class file
public Shape newShape() {
// call the other newShape method here -- invokevirtual newShape:()LCircle;

We can use javap with -c option on the class to verify this. Note that we still can't use return type based overloading in source language. But, this is used by javac to support covariant return types. This way, there is no change needed in the JVM to support covariant return types.

Be the first to comment

Comments ( 0 )
Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.Captcha