(?)通配符泛型类型的不规则

Cod*_*der 17 java generics unbounded-wildcard

我相信?泛型中的类型是一种特定的未知类型.这意味着,声明让我们说这种类型的列表会阻止我们在其中添加任何类型的对象.

List<?> unknownList;
unknownList.add(new Object()); // This is an error.
Run Code Online (Sandbox Code Playgroud)

编译器按预期给出错误.

但是当未知类型是二级泛型时,编译器似乎并不关心.

class First<T> {}

List<First<?>> firstUnknownList;

// All these three work fine for some reason.
firstUnknownList.add(new First<>());
firstUnknownList.add(new First<Integer>());
firstUnknownList.add(new First<String>());
Run Code Online (Sandbox Code Playgroud)

我想可能编译器根本不关心二级中的泛型参数,但事实并非如此,

List<First<Integer>> firstIntegerList;
firstIntegerList.add(new First<String>()); // This gives a compiler error as expected.
Run Code Online (Sandbox Code Playgroud)

那么,为什么编译器允许我们在第二个例子中只接受一个未知元素(因而没有任何东西)时添加任何类型的元素?

注意:编译器Java 1.8

And*_*ner 13

您可以List<T>在类型的引用中添加任何内容T:

T item = ...
List<T> list = new ArrayList<>();
list.add(item);
Run Code Online (Sandbox Code Playgroud)

First<?>是超类型First<T>; 所以你可以First<T>在一个类型的变量中存储对a的引用First<?>:

First<?> first = new First<String>();
Run Code Online (Sandbox Code Playgroud)

因此,取代TFirst<?>以上:

First<?> item = new First<String>();
List<First<?>> list = new ArrayList<>();
list.add(item);
Run Code Online (Sandbox Code Playgroud)

OP的例子中发生的所有事情item都是省略了临时变量:

firstUnknownList.add(new First<String>());
Run Code Online (Sandbox Code Playgroud)

但是,如果您使用firstIntegerList示例执行此操作:

First<Integer> item = new First<String>(); // Compiler error.
List<First<Integer>> list = new ArrayList<>();
list.add(item);
Run Code Online (Sandbox Code Playgroud)

很清楚为什么不允许这样做:你无法完成任务item.


也可以看到你不能对该列表的内容做任何不安全的事情.

如果向界面添加几个方法:

interface First<T> {
  T producer();
  void consumer(T in);
}
Run Code Online (Sandbox Code Playgroud)

现在,考虑一下您可以对添加到列表中的元素执行的操作:

for (First<?> first : firstUnknownList) {
  // OK at compile time; OK at runtime unless the method throws an exception.
  Object obj = first.producer();

  // OK at compile time; may fail at runtime if null is not an acceptable parameter.
  first.consumer(null);

  // Compiler error - you can't have a reference to a ?.
  first.consumer(/* some maybe non-null value */);
}
Run Code Online (Sandbox Code Playgroud)

所以实际上没有任何东西你可以真正做到该列表中违反类型安全的元素(假如你不做任何故意违反它,比如使用原始类型).您可以证明编译器同样安全或禁止通用生产者/消费者方法.

所以没有理由来让你做到这一点.