Java通配符列表和通配符框列表之间的泛型区别?

Java Generics Difference between List of Wildcard and List of Box of Wildcard?

为什么是

List<?> list = new ArrayList<Object>();

同时

List<Box<?>> boxList = new ArrayList<Box<Object>>();

不允许

还有List<? extends Box<?>>是什么意思。

因为 List<Box<?>> 接受包含任何内容的框。因此以下内容有效:

List<Box<?>> boxes = new ArrayList<>();
boxes.add(new Box<Integer>());
boxes.add(new Box<String>());

List<Box<Object>> 只接受 Box<Object> 的实例。因此以下内容无效,因为 Box<Integer>Box<String> 不是 Box<Object>.

List<Box<Object>> boxes = new ArrayList<>();
boxes.add(new Box<Integer>());
boxes.add(new Box<String>());

现在,如果您的问题是 "why is Box<String> not a Box<Object>",那么答案是,如果是,您可以执行以下操作,这会破坏泛型类型的类型安全性:

Box<Integer> boxOfIntegers = new Box<>();
Box<Object> box = boxOfIntegers; // this is actually invalid
box.add("I'm a string in a box of integers. Ouch!");

你的问题是一个经典的通用难题。通常我们的大脑足够聪明,可以从已知规则中推断出新规则,但泛型并非如此。您的问题说明了通配符的两个常见问题:

  • 您不能将 List<Box<Object>> 分配给 List<Box<?>>
  • 您实际上可以在 List<Box<?>> 上调用 add(),而不能在 List<?>
  • 上调用

第一个问题的解决方案是泛型是不变的:没有子类型关系,无论类型变量是什么类型,除非涉及通配符。即使 Box<Object>Box<?> 的子类型,子类型关系也不适用于封闭列表。

问题 #2 的规则是通配符不会被 捕获 递归。事实上 new ArrayList<Box<?>>() 是一个有效的表达式,而 new ArrayList<?>() 则不是。另外,正如我之前所说,您可以向 List<Box<?>> 添加元素,因为泛型类型 List<E> 的实例化会产生:

// This actually doesn't compile, it's just an explanation of the
// instantiation process
public interface List<Box<?>> {
  void add(Box<?> box);
}

List<A>List<?> 的子类型,其中 A 是一种类型(满足通配符的边界,如果有的话)。

但是,List<A> 不是 List<B> 的子类型,如果 AB 是不同的类型(无论 AB).

您可能已经看到 List<String> 不是 List<Object> 的子类型的情况,即使 StringObject 的子类型。 (这里就不深究原因了。)

即使 Box<String>Box<?> 的子类型,但 List<Box<String>> 不是 List<Box<?>> 的子类型时也会发生同样的事情。请注意,即使某处有一个 ?,它作为 Box<?> 类型的一部分在深处。 ?不作用于List的参数层级,所以它不允许你取不同类型的列表。

如果你想要一个可以接受 List<Box<String>>List<Box<Integer>> 等的类型,你可能想要使用 List<? extends Box<?>>。请注意,这里在顶层有一个 ?。这允许 List 的类型参数是 Box<?> 的任何子类型,其中包括 Box<String>Box<Integer>.