在 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,它是所有装箱类型的父类(super class)。在构造函数中,它采用一个声明为 E 类型(在本例中为 Number)的参数。另一方面,在 setValue
方法中,它采用一个参数,该参数被声明为任何扩展 E 类型。两个 println 调用都返回正确的值(第一个 java.lang.Long
,然后是 java.lang.Double
)。
在我看来,去掉泛型,你仍然可以传递子类。如果声明的类没有类型参数并且方法/构造函数采用/返回数字,它仍然可以正常工作。
那么在这种情况下 extends
关键字的目的是什么?
最佳答案
在这种情况下,使用 extends
没有任何好处。在 setValue
.你是对的 T
由 setValue
声明可以用它的上限代替,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
.
关于java - 为什么Java方法参数可以显式协变?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25717291/