为什么Java方法参数可以显式协变?

Mor*_*tch 3 java generics covariance

在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());
    }
}
Run Code Online (Sandbox Code Playgroud)

使用的类型是Number,所有盒装类型的超类.在构造函数中,它接受一个声明为E类型的参数(在本例中为Number).setValue另一方面,在该方法中,它采用一个声明为任何扩展类型E 的参数.两个println调用都返回正确的值(首先java.lang.Long,然后java.lang.Double).

我看到它的方式,删除泛型,你仍然可以传递子类.如果声明的类没有类型参数,并且方法/构造函数接受/返回Numbers,它仍然可以正常工作.

那么extends在这种情况下关键字的目的是什么?

Pau*_*ora 5

在这种情况下,使用extendsin 没有任何好处setValue.你T声明的声明setValue可以用它的上限代替你是对的,E:

public void setValue(E value) {
    this.value = value;
}
Run Code Online (Sandbox Code Playgroud)

但请考虑一下:

public <T extends E> T setValueAndGiveItBack(T value) {
    this.value = value;
    return value;
}
Run Code Online (Sandbox Code Playgroud)

这意味着我们可以这样做:

Double d = example.setValueAndGiveItBack(32.0);
Run Code Online (Sandbox Code Playgroud)

没有T,最具体的类型example.setValueAndGiveItBack可以返回Number.

只是为了澄清,你也是对的,类型参数本质上是协变的.使用的原因extends是限制类型参数的上限.例如,<T>隐含地<T extends Object>.在上面的例子中,没有声明ET上限,我们将无法分配value,this.value因为它可以是任何Object.