Why are Java method parameters explicitly covariant?

In Java, you can use a keyword extends

to declare that a given type parameter is covariant. Covariance and contravariance confuse me a little, but I think I got a general idea of ​​them. However, in my tests it seems that Java type parameters are inherently covariant. So why can we state this explicitly?

For example, I built a quick sample that looks like this:

public class Main<E> {

    protected E value;

    public Main(E value) {
        this.value = value;
    }

    public <T extends E> void setValue(T value) {
        this.value = value;
    }

    public E getValue() {
        return value;
    }

    public static void main(String[] args) {
        Main<Number> example = new Main<Number>(0L);
        System.out.println(example.getValue().getClass().getCanonicalName());
        example.setValue(32.0);
        System.out.println(example.getValue().getClass().getCanonicalName());
    }
}

      

The type used is Number, the superclass of all boxed types. In the constructor, it takes a parameter that is declared of type E (in this case, Number). On the other hand, in a method, setValue

it takes a parameter, which is declared as an extension of type E. Both calls to println return the correct value (first java.lang.Long

, then java.lang.Double

).

The way I see it, by removing generics, you can still pass in subclasses. If the class was declared with no type parameters, and the method / constructor took / returned Numbers, it would still work correctly.

So what is the purpose of the keyword extends

in this case?

+3


source to share


1 answer


In this case, there is no benefit to use extends

in setValue

. You are right that T

declared setValue

can be replaced by its upper limit E

:

public void setValue(E value) {
    this.value = value;
}

      

But consider this instead:

public <T extends E> T setValueAndGiveItBack(T value) {
    this.value = value;
    return value;
}

      



This means that we can do this:

Double d = example.setValueAndGiveItBack(32.0);

      

Without, the T

most likely type example.setValueAndGiveItBack

could be Number

.

To clarify, you are also correct that type parameters are inherently covariant. The reason for using it extends

is to restrict the upper bound of the type parameter. For example, <T>

implicitly <T extends Object>

. In the example above, without declaring it E

as an T

upper bound, we could not assign value

to this.value

, since it could be anything Object

.

+5


source







All Articles