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