泛型,类型参数和通配符

时间:2014-12-14 01:10:12

标签: java generics types wildcard

我正在尝试理解java泛型,它们似乎非常难以理解。例如,这很好......

public class Main {

    public static void main(String[] args) {
        List<?> list = null;
        method(list);
    }

    public static <T> void method(List<T> list) { }
}

......就是这样......

public class Main {

    public static void main(String[] args) {
        List<List<?>> list = null;
        method(list);
    }

    public static <T> void method(List<T> list) { }
}

......而这......

public class Main {

    public static void main(String[] args) {
        List<List<List<?>>> list = null;
        method(list);
    }

    public static <T> void method(List<List<T>> list) { }
}

......但这不会编译:

public class Main {

    public static void main(String[] args) {
        List<List<?>> list = null;
        method(list);
    }

    public static <T> void method(List<List<T>> list) { }
}

有人可以用简单的语言解释发生了什么吗?

1 个答案:

答案 0 :(得分:14)

通用类型要理解的主要问题是它们不是协变的。

所以虽然你可以这样做:

final String string = "string";
final Object object = string;

以下内容无法编译:

final List<String> strings = ...
final List<Object> objects = strings;

这是为了避免你绕过泛型类型的情况:

final List<String> strings = ...
final List<Object> objects = strings;
objects.add(1);
final String string = strings.get(0); <-- oops

所以,逐个浏览你的例子

1

您的通用方法需要List<T>,您传入List<?>;它(基本上)是List<Object>。可以将T分配给Object类型,编译器也很满意。

2

您的通用方法是相同的,您传入List<List<?>>。可以将T分配给List<?>类型,编译器也很高兴。

3

这与其他嵌套级别的 2 基本相同。 T仍然是List<?>类型。

4

这是一个小梨形的地方,以及我从上面的观点出现的地方。

您的通用方法需要List<List<T>>。你传递List<List<?>>。现在,由于泛型类型不是协变的,因此无法将List<?>分配给List<T>

实际的编译器错误(Java 8)是:

  

必填:java.util.List<java.util.List<T>>找到:   java.util.List<java.util.List<?>>原因:无法推断   type-variable(s)T       (参数不匹配; java.util.List<java.util.List<?>>无法转换为java.util.List<java.util.List<T>>

基本上编译器告诉你它找不到要分配的T,因为必须推断嵌套在外部列表中的List<T>的类型。

让我们更详细地看一下这个:

List<?>某种未知类型的List - 可以是List<Integer>List<String>;我们get可以Object,但我们不能add 。因为否则我们遇到了我提到的协方差问题。

List<List<?>>是某个未知类型List的{​​{1}} - 可能是ListList<List<Integer>>。如果 1 ,则可以将List<List<String>>分配给T,并且不允许对通配符列表执行Object次操作。如果 4 ,则无法执行此操作 - 主要是因为没有通用构造来阻止add到外部add

如果编译器要在第二种情况下将List分配给T,则可能会出现以下内容:

Object

因此,由于协方差,无法安全地将final List<List<Integer>> list = ... final List<List<?>> wildcard = list; wildcard.add(Arrays.asList("oops")); 分配给任何其他通用List<List<Integer>>