java 泛型中的通配符

Wildcards in java Generics

我知道泛型是不变的:对于任何两种不同的类型 Type1 和 Type2, List 既不是 List< 的子类型也不是超类型 类型 2>

所以

List<someObject> nums = new ArrayList<someObject>(); // this is correct
List<Object> nums = new ArrayList<subObject>(); // this is  not correct

但是

List<Number> nums = new ArrayList<Number>();
List<? super Number> sink = nums;   ????(line 2)

假设通配符是对象,那么第 2 行将是

List<Object> sink = List<Number> nums 

这里似乎没有应用不变规则

谁能给我解释一下为什么第 2 行编译没有错误?

非常感谢

让我们有:

List<Integer> li = new ArrayList<>();
List<Object> lo = new ArrayList<>();
l = lo;//not allowed
lo.add("string");
Integer i = li.get(0);//that is string there

这就是为什么你不能做这样的分配。我认为编译器不会处理 new ArrayList<Integer>() 的直接赋值,而不是现有变量 li.

不变规则确实适用。声明:

List<? super Number> sink = nums; 

只是行不通,你想的那样。

我希望你认为可以将ArrayList类型的对象赋值给List<? super Number>类型的变量,因为前者是后者的子类。这将打破不变规则,但这不是正在发生的事情。

List<? super Number> 变量表示所有可能列表的集合,其中 someClass 是 Number 或者是 Number 的祖先。

let say if wildcard is Object so line 2 wil be

     `List<Object> sink = List<Number> nums `

在这种情况下?不会被任意设置,所以这不会发生。

如果我没记错的话,你希望对下面的有效代码有一个解释:

List<Number> nums = new ArrayList<Number>();
List<? super Number> sink = nums;

不变性是 属性 的 class 关于其类型参数如何影响其子类型。

泛型是不变的,但通配符的存在可以帮助我们进行子类型化。它们不是很有用,因为它们不代表任何类型,但代表一个很好的 hack。以下有效

List<Animal>  <: List<?>
List<?>       <: List

更好的例子:

List<? super Animal> d1= new ArrayList<Animal>();
d1.add(new Animal());
d1.add(new Dog());

以上有效,因为 d1List<? super Animal> 类型。您可以想象 add 函数的行为类似于:

boolean add(? super Animal e);

因此,对于 add 方法,您可以传递属于 ? super Animal 子类型 的任何变量。并且

Animal <: ? super Animal
Dog    <: ? super Animal

所以添加一只狗或动物是可行的。所以 d1 充当一个列表,可以采用 Animal 类型或子类型的任何参数。

同样,你也可以有下面的。但从技术上讲,您不能向此列表中添加任何内容。如果 java 中存在某种类型,它是每种类型的子类型,那么您可以适当地向其中添加该类型的元素。没有别的。

ArrayList<? extends Animal> d1  = new ArrayList<Animal>();

查看此答案了解更多信息 info