为什么我不能将Collection <GenericFoo>转换为Collection <GenericFoo <?>>

ysh*_*vit 17 java generics

问题的关键是,为什么这会导致编译时错误?

List<Collection> raws = new ArrayList<Collection>();
List<Collection<?>> c = raws; // error
Run Code Online (Sandbox Code Playgroud)

背景

我理解为什么泛型一般不协变.如果我们能够分配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
Run Code Online (Sandbox Code Playgroud)

我们在第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();
Run Code Online (Sandbox Code Playgroud)

在这两种情况下,最终我只获得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
Run Code Online (Sandbox Code Playgroud)

错误是因为GenericFoo.class有类型Class<GenericFoo>,而不是Class<GenericFoo<?>>(§15.8.2).虽然我怀疑这是一个相关的原因,但我不确定为什么会这样.但无论如何,如果Class<GenericFoo>可以通过隐式或明确的方式进行转换,那将不会成为问题Class<GenericFoo<?>>.

Zho*_*gYu 2

首先,原始类型和通配符类型有很大不同。其一,原始类型完全删除了所有通用信息。

所以我们有List<x>List<y>其中 x 不是 y。这当然不是子类型关系。

不过,您可以请求允许选角。但请阅读 JLS 5.5.1,并告诉我你想在其中添加更多内容:) 浏览整个页面,实际上,这是一堵巨大的文本墙,仅用于投射

请记住,这只是整个效果中的第一个涟漪。以及List<List<x>>等等List<List<y>>呢?