我对Java泛型如何处理继承/多态性感到困惑.
假设以下层次结构 -
动物(父母)
狗 - 猫(儿童)
所以假设我有一个方法doSomething(List<Animal> animals).根据所有继承和多态的规则,我会假设a List<Dog> 是 a List<Animal>而a List<Cat> 是 a List<Animal>- 所以任何一个都可以传递给这个方法.不是这样.如果我想实现这种行为,我必须明确告诉该方法接受一个Animal的任何子类的列表doSomething(List<? extends Animal> animals).
我知道这是Java的行为.我的问题是为什么?为什么多态通常是隐含的,但是当涉及泛型时必须指定它?
原始列表转换为List<?>正常.为什么原始列表列表不能转换为列表List<?>?
{ // works
List raw = null;
List<?> wild = raw;
}
{ // Type mismatch: cannot convert from List<List> to List<List<?>>
List<List> raw = null;
List<List<?>> wild = raw;
}
Run Code Online (Sandbox Code Playgroud)
背景故事(缓解xy问题):
我正在使用的API返回List<JAXBElement>.我碰巧知道它总是如此List<JAXBElement<String>>.我计划循环并构建自己的List<String>,但我在写时试图修复(但不是抑制)原始类型编译器警告List<JAXBElement> raw = api();.
我试过了:
List<JAXBElement<?>> raw = api();
List<JAXBElement<?>> raw = (List<JAXBElement<?>>) api();
Run Code Online (Sandbox Code Playgroud)
但这些给出了类型不匹配错误.
有趣的是,这没有任何警告或错误:
for (JAXBElement<?> e : api()) {
// ...
}
Run Code Online (Sandbox Code Playgroud) 如果您在Java中具有原始类型,则可以使用无界通配符安全地将其分配/转换为相同类型.例如,a List可以安全地转换为a List<?>,这会消除其原始特性,并允许您以安全(类型检查)的方式使用它1.
在另一方面,Java并不让您从投List 本身参数与原始类型,比如List<Optional>同类型的参数列表中采用无界通配符,像List<Optional<?>>.
您仍然可以通过完全丢弃原始数据List并再次备份(通过赋值隐式)来执行此操作:
List<Optional> rawOptionalList = null;
List<Optional<?>> wildcardOptionalList = (List)rawOptionalList;
Run Code Online (Sandbox Code Playgroud)
当然,这会触发关于未经检查的转换(从List到List<Optional<?>>)的警告.
在我看来,虽然这种转换可以保证安全:List<Optional<?>>不仅仅像List<Optional>将原始产品Optional转换Optional<?>为安全一样安全吗?
1 ...但你永远无法在此列表中添加任何内容,因为没有任何内容与?该add(?)方法的捕获相匹配.这是你为安全付出的代价.