泛型,类型参数和通配符

Pau*_*ton 21 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) { }
}
Run Code Online (Sandbox Code Playgroud)

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

public class Main {

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

    public static <T> void method(List<T> list) { }
}
Run Code Online (Sandbox Code Playgroud)

... 还有这个 ...

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) { }
}
Run Code Online (Sandbox Code Playgroud)

...但这不编译:

public class Main {

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

    public static <T> void method(List<List<T>> list) { }
}
Run Code Online (Sandbox Code Playgroud)

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

Bor*_*der 14

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

所以你可以这样做:

final String string = "string";
final Object object = string;
Run Code Online (Sandbox Code Playgroud)

以下内容无法编译:

final List<String> strings = ...
final List<Object> objects = strings;
Run Code Online (Sandbox Code Playgroud)

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

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

所以,逐个浏览你的例子

1

你的通用方法需要一个List<T>,你传入一个List<?>; 这是(基本上)a List<Object>.T可以分配给Object类型,编译器很高兴.

2

你的通用方法是一样的,你传入一个List<List<?>>.T可以分配给List<?>类型,编译器也很高兴.

3

这与另一个嵌套级别的2基本相同.T仍然是List<?>那种类型.

4

这里是一个小梨形的地方,我从上面的点进来.

你的通用方法需要一个List<List<T>>.你传了一个List<List<?>>.现在,由于泛型类型不协变,List<?>因此无法分配给a List<T>.

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

required:java.util.List<java.util.List<T>> found: java.util.List<java.util.List<?>> reason:无法推断类型变量(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<?>>ListList一些未知类型的-它可能是一个List<List<Integer>>或一个List<List<String>>.如果1是可以分配TObject,只是不允许add通配符列表上的操作.在案例4中,这不可能完成 - 主要是因为没有一个泛型构造来防止add外部List.

如果编译器在第二种情况下分配TObject那么类似下面的东西是可能的:

final List<List<Integer>> list = ...
final List<List<?>> wildcard = list;
wildcard.add(Arrays.asList("oops"));
Run Code Online (Sandbox Code Playgroud)

因此,由于协方差,不可能安全地分配List<List<Integer>>任何其他通用List.

  • 你的意思是`objects.add(1);`? (2认同)