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

标签 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,它是所有装箱类型的父类(super class)。在构造函数中,它采用一个声明为 E 类型(在本例中为 Number)的参数。另一方面,在 setValue 方法中,它采用一个参数,该参数被声明为任何扩展 E 类型。两个 println 调用都返回正确的值(第一个 java.lang.Long,然后是 java.lang.Double)。

在我看来,去掉泛型,你仍然可以传递子类。如果声明的类没有类型参数并且方法/构造函数采用/返回数字,它仍然可以正常工作。

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

最佳答案

在这种情况下,使用 extends 没有任何好处。在 setValue .你是对的 TsetValue 声明可以用它的上限代替,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的上限,我们将无法分配 valuethis.value因为它可以是任何 Object .

关于java - 为什么Java方法参数可以显式协变?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25717291/

相关文章:

java - 如何在 UI 中编辑数据时警告用户,以便我可以警告他们覆盖或提示他们保存? ( java )

java - ArrayList<anyClassObject> 的动态初始化

java - 抽象方法中的通用列表

java - Java 通配符泛型 <?> 的 .NET 等效项具有协方差和逆方差?

java - 在 Android 中引用文件时遇到问题

java - 为现有插件创建附加组件

java - 如何确定 Java 中泛型字段的类型?

c++ - 为什么我不能拥有与 void* 协变的返回类型?

c# - 转换泛型和泛型类型

java - 如何将字符串转换为按类型