问题的症结在于,为什么会导致编译时错误?
List<Collection> raws = new ArrayList<Collection>();
List<Collection<?>> c = raws; // error
我理解为什么泛型一般不协变。如果我们可以将List<Integer>
分配给List<Number>
,我们会将自己暴露给ClassCastExceptions:
List<Integer> ints = new ArrayList<Integer>();
List<Number> nums = ints; // compile-time error
nums.add(Double.valueOf(1.2));
Integer i = ints.get(0); // ClassCastException
我们在第2行遇到编译时错误,使我们免于第4行的运行时错误。这是有道理的。
List<C>
至List<C<?>>
但是这个怎么样:
List<Collection> rawLists = new ArrayList<Collection>();
List<Collection<?>> wildLists = rawLists; // compile-time error
// scenario 1: add to raw and get from wild
rawLists.add(new ArrayList<Integer>());
Collection<?> c1 = wildLists.get(0);
Object o1 = c1.iterator().next();
// scenario 2: add to wild and get from raw
wildLists.add(new ArrayList<String>());
Collection c2 = rawLists.get(0);
Object o2 = c2.iterator().next();
在这两种情况下,最终我只获得Object
元素而不进行强制转换,因此我无法获得“神秘”的ClassCastException。
JLS中与此对应的部分是§4.10.2,所以我理解为什么编译器会给我错误;我没有得到的是为什么规范以这种方式编写,以及(以避免投机/基于意见的答案),它是否实际上为我提供了编译时的安全性。
如果你想知道,这是(一个简化的版本)用例:
public Collection<T> readJsons(List<String> jsons, Class<T> clazz) {
List<T> list = new ArrayList<T>();
for (String json : jsons) {
T elem = jsonMapper.readAs(json, clazz);
list.add(elem);
}
return list;
}
// call site
List<GenericFoo<?>> foos = readJsons(GenericFoo.class); // error
错误是因为GenericFoo.class
的类型为Class<GenericFoo>
,而非Class<GenericFoo<?>>
(§15.8.2)。虽然我怀疑这是一个相关的原因,但我不确定为什么会这样。但无论如何,如果Class<GenericFoo>
可以隐式或明确地投放到Class<GenericFoo<?>>
,那就不会有问题。
答案 0 :(得分:2)
首先,原始类型和通配符类型是完全不同的。例如,原始类型完全删除所有通用信息。
所以我们有List<x>
和List<y>
,其中x不是y。这当然不是亚型关系。
然而,你可以要求允许铸造。但请阅读 JLS 5.5.1,并告诉我你想要添加更多内容:)浏览整个页面,实际上,它只是用于投射的文字墙。
请记住,这只是整个效果中的第一次涟漪。那么List<List<x>>
和List<List<y>>
等等