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>
的子类型,如果 A
和 B
是不同的类型(无论 A
和 B
).
您可能已经看到 List<String>
不是 List<Object>
的子类型的情况,即使 String
是 Object
的子类型。 (这里就不深究原因了。)
即使 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>
.
为什么是
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>
的子类型,如果 A
和 B
是不同的类型(无论 A
和 B
).
您可能已经看到 List<String>
不是 List<Object>
的子类型的情况,即使 String
是 Object
的子类型。 (这里就不深究原因了。)
即使 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>
.