泛型、类型参数和通配符 [英] Generics, Type Parameters and Wildcards

查看:21
本文介绍了泛型、类型参数和通配符的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am trying to understand java generics and they seem extremely difficult to understand. For example, this is fine...

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) { }
}

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

Can someone explain what is going on in simple language?

推荐答案

要理解泛型类型的主要内容是它们不是协变的.

The main thing to understand with generic types, is that they aren't covariant.

所以虽然你可以这样做:

So whilst you can do this:

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

以下不会编译:

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

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

This is to avoid the situations where you circumvent the generic types:

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

所以,一个一个地浏览你的例子

So, going through your examples one by one

你的泛型方法需要一个List,你传入一个List;它(本质上)是一个 List.T 可以赋值给 Object 类型,编译器很开心.

Your generic method takes a List<T>, you pass in a List<?>; which is (essentially) a List<Object>. T can be assigned to the Object type and the compiler is happy.

你的通用方法是一样的,你传入一个List.T 可以分配给 List 类型,编译器又开心了.

Your generic method is the same, you pass in a List<List<?>>. T can be assigned to the List<?> type and the compiler is again happy.

这与 2 基本相同,只是多了一层嵌套.T 仍然是 List 类型.

This is basically the same as 2 with another level of nesting. T is still the List<?> type.

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

Here is where it goes a little pear shaped, and where my point from above comes in.

您的通用方法采用 List>.你传入一个 List>.现在,由于泛型类型不是协变的,List 不能分配给 List.

Your generic method takes a List<List<T>>. You pass in a List<List<?>>. Now, as generic types are not covariant, List<?> cannot be assigned to a List<T>.

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

The actual compiler error (Java 8) is:

必需:java.util.List> 找到:java.util.List> 原因:无法推断类型变量T(参数不匹配;java.util.List> 无法转换为 java.util.List>;)

required: java.util.List<java.util.List<T>> found: java.util.List<java.util.List<?>> reason: cannot infer type-variable(s) T (argument mismatch; java.util.List<java.util.List<?>> cannot be converted to java.util.List<java.util.List<T>>)

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

Basically the compiler is telling you that it cannot find a T to assign because of having to infer the type of the List<T> nested in the outer list.

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

Lets look at this in a little more detail:

List某种未知类型List - 它可能是一个 ListList;我们可以从它get作为Object但我们不能add.因为否则我们就会遇到我提到的协方差问题.

List<?> is a List of some unknown type - it could be a List<Integer> or a List<String>; we can get from it as Object, but we cannot add. Because otherwise we run into the covariance issue I mentioned.

List<List 是某种未知类型的 ListList - 它可能是一个 List>List>.在 1 的情况下,可以将 T 分配给 Object 并且不允许对通配符列表进行 add 操作.如果 4 不能这样做 - 主要是因为没有泛型结构来防止 add 到外部 List.

List<List<?>> is a List of List of some unknown type - it could be a List<List<Integer>> or a List<List<String>>. In case 1 it was possible to assign T to Object and just not allow add operations on wildcard list. In case 4 this cannot be done - primarily because there is not a generics construct to prevent add to the outer List.

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

If the compiler were to assign T to Object in the second case then something like the following would be possible:

final List<List<Integer>> list = ...
final List<List<?>> wildcard = list;
wildcard.add(Arrays.asList("oops"));

因此,由于协方差,不可能将 List> 分配给 任何其他泛型 List安全.

So, due to covariance, it is not possible to assign a List<List<Integer>> to any other generic List safely.

这篇关于泛型、类型参数和通配符的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆