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

为什么Java方法参数可以显式协变?,java,generics,covariance,Java,Generics,Covariance,在Java中,可以使用extends关键字声明给定类型参数是协变的。协方差和逆方差确实让我有点困惑,但我想我已经大致了解了它们。然而,在我的测试中,似乎Java类型参数本质上是协变的。那么,为什么我们可以明确地说明这一点呢 例如,我构建了一个如下所示的快速示例: public class Main<E> { protected E value; public Main(E value) { this.value = value; }

在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());
    }
}
公共类主{
保护E值;
公用干管(E值){
这个值=值;
}
公共无效设置值(T值){
这个值=值;
}
公共E getValue(){
返回值;
}
公共静态void main(字符串[]args){
主示例=新主(0升);
System.out.println(例如.getValue().getClass().getCanonicalName());
例如,设置值(32.0);
System.out.println(例如.getValue().getClass().getCanonicalName());
}
}
使用的类型是Number,它是所有装箱类型的超类。在构造函数中,它接受一个声明为E类型的参数(在本例中为数字)。另一方面,在
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;
}
如果没有
T
,最具体的类型
示例.setValueAndGiveItBack
可以返回
Number


只是澄清一下,类型参数本质上是协变的,这一点也是正确的。使用
extends
的原因是限制类型参数的上限。例如,
是隐式的
。在上面的示例中,如果不将
E
声明为
T
的上限,我们将无法将
分配给
此.value
,因为它可以是任何有意义的
对象,谢谢!我试图编写一个系统,它实际使用的值与此类似,基类是泛型的,并持有一个类型为“E”的值,然后子类在此基础上展开。我并没有真正理解这一点,我继续使用了基本上所有的方法,认为我需要使用一些类似于
setValue()
的方法。现在我明白了,我真的不知道。这应该有助于清理我的代码很多,谢谢!
Double d = example.setValueAndGiveItBack(32.0);