Java 仿制药 - 什么时候?需要通配符

Java generics - When is ? wildcard needed

假设你有一些 class 类型 T:

class MyClass<T extends SomeOtherClass> {
    ....
}

现在,您想将此 class 的实例存储到一个集合中,但您并不真正关心类型。我会表达如下:

private final List<MyClass> entries = new ArrayList<>();

有什么好的reason/advantage改写下面的吗?

private final List<MyClass<?>> entries = new ArrayList<>();

甚至:

private final List<MyClass<? extends SomeOtherClass> entries = new ArrayList<>();

我自己只能找到一个不好的理由这样做:每当 MyClass 的类型定义发生变化(例如添加另一种类型)时,您必须更改 List<MyClass<?>>List<MyClass<? extends SomeOtherClass>> 定义遍布你的代码。

更新

更新我的问题:

为什么编译器无法在您编写 List<MyClass>(甚至 List<MyClass<? extends SomeOtherClass>>)时跟踪 MyClass 的类型?他知道 MyClass 被定义为 MyClass<T extends SomeOtherClass>,所以当你写 List<MyClass> 时他 able/allowed 为什么不这样做?

换句话说,为什么List<MyClass>不等于List<MyClass<?>>(甚至List<MyClass<? extends SomeOtherClass>>)?编译器拥有自己得出结论的所有信息,afaik。

你的第一个声明意味着,你没有为未来的反射系统提供任何关于你的通用数据的信息,这些信息可以被为你的主程序编写的插件使用。

第二个声明告诉他们,该字段包含一个对象泛型。

第三个更具体,这意味着反射系统知道这个字段的详细信息。

? 通配符在您不需要再次引用类型时非常有用,因此您不需要创建特定的标识符。

您的第一个片段以 class MyClass<T extends SomeOtherClass> 开头。当 T 稍后很重要时,这是必要的,可能声明字段参数或 return 类型。例如:

class MyClass<T extends SomeOtherClass> {
    private final List<T> aListOfT;
    public T getSomething() {
        return this.aListOfT.get(0);
    }

由于 MyClass 是一个泛型类型,所有对它的引用都应该被限定以避免可避免的运行时错误。因此,当您声明 List<MyClass> 时,您会收到一个编译器警告,提示您使用原始类型 MyClass。如果您不关心代码中的那个位置 MyClass 限定了哪种类型,那么您可以使用 ? 告诉编译器您不关心并让它跟踪类型并检查所有操作的有效性。

使用第一种类型,java 将假定泛型类型为 Object。这样做的原因是,泛型是在 1.5 版中引入的。在此之前,collection-类 将所有内容都存储为一个对象。出于兼容性原因,不提供通用参数意味着您正在使用对象。

第二种就是说你不知道或者不关心它是什么类型。编译代码时会保留此信息。所以其他可能使用你的代码的程序员会知道你不在乎。

由于java的类型擦除,这两者在运行时没有区别。

最后一个表格你说: 我不在乎它是什么,但它必须是 SomeOtherClass 或派生类型。与以下内容相同:

List<MyClass<SomeOtherClass>>

你也可以反过来做:

List<MyClass<? super SomeOtherClass>>

表示您不关心它是什么类型,除非它是 SomeOtherClass 的超类型。