使用带有上限通配符的声明
doSomething(List<? extends X>)
List<S>
不是的子类型List<T>
即使S
是的子类型T
您可以用非常简单的术语来思考为什么会这样。
如果Dog
和Cat
是 的子类型Animal
并且你的方法是doSomething(List<Animal> pets)
,那么在方法里面你可能add
是Cat
. 但是如果pets
作为 a 传递List<Dog>
呢?你可能会出现过敏反应。
输入通配符
因此,您可以使用通配符来执行此操作。通配符参数化类型 表示List<? extends Animal>
:“(来自)所有参数化类型的集合 - 通过调用泛型类型形成List<T>
- 类型参数是任何类型(通常称为“未知类型”) - 这是Animal
(包括的)”。
暗示这是一个潜在的无限集合,因为您可能有无限数量的子类型Animal
(和 List mumble mumble 的子类型使问题 mumble 复杂化)。
“在”参数
使用List<? extends Animal>
表示“我同意将此列表中的所有内容视为,充其量是一个Animal
”。简单来说,您不能再add(e)
对该列表进行任何操作,因为在方法内部您不知道它是 a还是List<Dog>
在调用站点。但是,您可以使用该列表参数中的任何项目和它(假设是 上的方法)。它是一个“ in ”参数:它为方法提供数据。List<Cat>
List<Animal>
get(index)
feed()
feed()
Animal
tldr:extends
边界意味着一个“输入”变量(您从中提取数据的变量,而不是您将数据放入的变量)
“出”参数
如果您想add(e)
进入Dog
列表,您需要通过声明将方法声明更改为“ out ”变量doSomething(List<? super Dog>)
。现在您在说“我可以将此列表视为包含任何超类型Dog
”的类型:在调用站点它可能是aList<Dog>
或. 您现在可以使用 a或 的子类型,例如 a : all 将与传递的任何可能的列表类型兼容。您不能添加 a ,因为这没有任何意义:调用站点的列表仍然可以是 a ,而不是 a 。List<Animal>
List<Object>
add(e)
Dog
Dog
Mongrel
Cat
List<Dog>
List<Animal>
tldr:它是一个“ out ”参数:它从方法内部“存储”数据,(因此可以将其提供回调用站点)。
参考:Java 教程 >上界通配符