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



在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);

如果没有Texample.setValueAndGiveItBack可以返回的最具体的类型将是Number

为了澄清,类型参数本质上是协变的,这一点也是正确的。使用extends的原因是为了限制类型参数的上限。例如,<T>隐含地为<T extends Object>。在上面的例子中,如果不将E声明为T的上界,我们将无法将value分配给this.value,因为它可以是任何Object

相关内容

  • 没有找到相关文章

最新更新