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?
source to share
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
.
source to share