我有类似的东西:
List<? extends BaseClass> a = getMyList();
以下两条指令中的任何一条都是无效的,编译器说它需要一个“? extends BaseClass”作为参数。
a.add(new BaseClass());
a.add(new SubClass());
我想问题是
如何解决?
应该是List<? super BaseClass> a
往里面加东西。你只能从List<? extends BaseClass> a
.
问题是编译器无法确定列表的实际类型。
可能类型是SubClass
,在这种情况下,您将不允许添加BaseClass
.
甚至可以是SubSubClass
,这意味着您不能添加BaseClass
或SubClass
。
但是,这将编译:
List<BaseClass> a = getMyList();
a.add(new BaseClass());
a.add(new SubClass());
通常 Java 教程说您不能向 a 添加任何内容,List<? extends Something>
因为编译器无法知道列表中的有效类型。
我发现这个解释不直观,也不是完全正确的,因为它假装编译器是一个聪明的存在,它理解 aList
是一个有序的元素容器,并防止你做潜在的不安全的事情。实际上,编译器只是一个程序,它只是遵循一些规则。
所以,最好做一些角色扮演,像编译器一样思考。你有
interface List<E> {
void add(E element);
}
请注意,<E>
它不会为编译器提供任何额外的语义,因为它不知道您正在定义容器类型。您可以定义 a Asdf<Q>
,并且对于编译器来说没关系,或者 anElephant<W>
并且仍然适用相同的规则(显然Elephant
不是容器类型,即您不向大象添加任何东西 - 我希望......)
所以你声明了一个 type 的引用List<? extends Shape>
。编译器理解类似的东西
abstract class Unknown extends Shape {}
class ListOfUnknown {
void add(Unknown element) {}
Unknown get(int index) {}
}
您能明白为什么不能将 a 添加Rectangle
到这样的列表中吗?根据正常的Java 规则,您可以Rectangle
为方法提供 a,例如add(Shape)
因为子类型关系确保它们的接口兼容。
允许add(Unknown)
使用 type 的参数进行调用Rectangle
,Rectangle
应该是 的子级Unknown
,但Rectangle
只能是 extends Shape
,因此这里的泛型没有什么特别之处,这是用于类型兼容性的普通 Java 规则。为了能够调用add(Unknown)
,您需要对类型Unknown
或等效对象(某些扩展的类Unknown
)的引用,但正如您所见,在任何地方都没有定义此类类型,因此这最终会禁止add()
此列表中的任何内容。
学习泛型时,总是问自己“为什么?” 并且永远不要停留在List
示例上,因为即使泛型是为集合添加的主要内容,它们也是一种语言特性,因此您必须了解它们的语义,而不是专注于容器类型的具体实现。