在Java中,可以使用extends
关键字来声明给定的类型参数是协变的。协变和逆变确实让我有点困惑,但我想我已经对它们有了大致的了解。然而,在我的测试中,Java类型参数似乎是固有的协变。那么,为什么我们可以明确地说明呢?
例如,我构建了一个快速样本,如下所示:
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());
}
}
使用的类型是Number,它是所有装箱类型的超类。在构造函数中,它接受一个类型为E(在本例中为Number)的参数。另一方面,在setValue
方法中,它接受一个参数,该参数被声明为扩展类型E的任何。两个println调用都返回正确的值(首先是java.lang.Long
,然后是java.lang.Double
)。
在我看来,删除泛型,仍然可以传递子类。如果该类是在没有类型参数的情况下声明的,并且方法/构造函数接受/返回数字,那么它仍然可以正常工作。
那么,在这种情况下extends
关键字的用途是什么呢?
在这种情况下,在setValue
中使用extends
没有任何好处。你说得对,setValue
声明的T
可以被它的上界E
:代替
public void setValue(E value) {
this.value = value;
}
但考虑一下:
public <T extends E> T setValueAndGiveItBack(T value) {
this.value = value;
return value;
}
这意味着我们可以做到:
Double d = example.setValueAndGiveItBack(32.0);
如果没有T
,example.setValueAndGiveItBack
可以返回的最具体的类型将是Number
。
为了澄清,类型参数本质上是协变的,这一点也是正确的。使用extends
的原因是为了限制类型参数的上限。例如,<T>
隐含地为<T extends Object>
。在上面的例子中,如果不将E
声明为T
的上界,我们将无法将value
分配给this.value
,因为它可以是任何Object
。