Java泛型方法,通配符List返回类型

时间:2016-09-14 10:22:25

标签: java generics

Java的8方法签名如下:

public static <E extends CharSequence> List<? super E> m(List<E> list);

在代码的某处,方法调用:

result = m(list);

假设我创建了参数list,如下所示:

List<String> list = new ArrayList<>();

我的问题是,为什么result可能只是原始List,甚至不能是List<Object>

2 个答案:

答案 0 :(得分:3)

使用List<String>作为参数调用方法就像调用以下方法一样:

public static List<? super String> m(List<String> list) { ... }

考虑以下起始代码:

List<String> list = new ArrayList<>();
List<? super String> result = m(list);

这将调用上面提到的方法并将返回值存储到变量result中 - 该变量使用方法的返回类型完全输入。

现在的问题是:您可以为这些变量分配哪些变量 - 或更好的类型?所以我们谈论的是赋值兼容性。

考虑这些作业:

List<String> result1 = result; // compiler error: type mismatch (not assignable)
List<Object> result2 = result; // compiler error: type mismatch (not assignable)

List result3 = result; // ok
List<?> result4 = result; // ok

List<? super String> result5 = result; // ok
List<? extends Object> result6 = result; // ok

要了解此错误的性质,您必须知道泛型不变。这意味着,类型List<String> 不是 List<Object>的子类型 - 尽管类型StringObject具有这样的子类型层次结构。

所以,我们在这里尝试的是:

  1. List<? super String>分配给List<String> =&gt;失败,没有子类型
  2. List<? super String>分配给List<Object> =&gt;失败,没有子类型
  3. List<? super String>分配给List =&gt;成功,因为使用原始类型通常会从类型检查中选择
  4. List<? super String>分配给List<?> =&gt;成功,因为List<?>是所有List<...>
  5. 的超类型
  6. List<? super String>分配给List<? super String> =&gt;成功,因为......好吧......
  7. List<? super String>分配给List<? ectends Object> =&gt;成功,因为List<? ectends Object>List<?>基本相同。
  8. 注意,尝试将List<? super String>分配给List<? super CharSequence>List<? extends CharSequence>也会失败。它们不在子类型层次结构中。

    为什么会这样?编译器无法保证实际列表是使用与约束? super/extends CharSequence匹配的类型实例化的。

答案 1 :(得分:1)

您无法做到这一点的原因是因为Java泛型不是协变的。也就是说,List<Object> List<String>的超类。然而,方法m的方法签名允许返回List<String>,然后您会有一个List<Object>类型的引用指向List<String>。由于泛型类型信息在运行时不可用,因此不会立即引发异常,但可能会导致不相关位置出现类型错误。

例如,假设m实现如下:

private static List<CharSequence> theResult = new ArrayList<>();

public static <E extends CharSequence> List<? super E> m(List<E> list) {
    theResult.addAll(list);
    return theResult;
}

然后使用您的方法,将非CharSequence对象添加到List<CharSequence>

List<Object> os = m(someList);
os.add(new Object());

幸运的是,以上内容无法编译。