Search code examples
javareturn-typecovariant

Java Co-variant returns


In java why are covariant return types acceptable when non-covariant return types produce a compile time error. Surely if the JVM can handle covariant return types then it can handle noncovariant return types. I presume that when java sees an overridden method with a covariant return it just applies the method that's associated with the calling object. Why can't the same happen with non covariant return types. My guess is that it's to do with breaking the terms of the superclass' method contract and of course if this were allowed then the behavior of the sub class (overridden) methods aren't very predictable (since there is no consistency in return type)?

Here's an example (assume that DogFood is a subclass of Food but CatFood is not a subclass of Food):

Animal class

public class Animal {

public Food seekFood() {

    return new Food();
}
}

Dog class

public class Dog extends Animal {

public DogFood seekFood() { //This is OK since its a covariant

    return new DogFood();
}
}

Cat class

    public class Cat extends Animal {

public CatFood seekFood() { // This won't compile. Catfood is not covariant

    return new CatFood();
}
}

Solution

  • class A {
        Ra f(Pa x) { ... }
    }
    
    class B extends A {
        @Override
        Rb f(Pb x) { ... }
    }
    

    The inheritance rule determines the co-variant/contra-variant behavior:

    A value of type T may only assigned to a variable of type T or parent type.

    A method call implies assigning its actual method arguments (1) to local parameters (2), and assigning the result value (1) to some location (2) to be used.

    Now if the compiler meets an A object which actually can be a B, then it follows for B.f to be a valid override:

    1. Pb may only be valid when it Pa or a parent class (contra-variant);

      Because you B.f must be able to receive at least a Pa value.

    2. Rb may only be valid when it is Ra or a child class (co-variant);

      Because B.f must return something that is assignable to an Ra.

    This makes the child class more restrictive, specific.

    On the case at hand, a Cat may return DogFood when DogFood is a child of Food. So any Animal's Food is indeed a Food, even if the Animal is actual a Cat.