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

时间:2014-09-08 04:03:09

标签: 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());
    }
}

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

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

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

1 个答案:

答案 0 :(得分:5)

在这种情况下,在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 {{1}} }}