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

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

问题描述

我试图理解java泛型,它们看起来非常难以理解。例如,这是好的......

  public class Main {

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

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

... as as这... ...

pre $ $ code $ public $ {

public static void main(String [] args) {
List< List<>> list = null;
方法(列表);
}

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

...和this ...

pre $ public class Main {

public static void main(String [] args){
List< List< List<>>> list = null;
方法(列表);
}

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

..但这不能编译:

  public class Main {

public static void main String [] args){
List< List<>> list = null;
方法(列表);
}

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

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

解决方案

使用泛型类型理解的主要原因是它们不是协变的。

所以,尽管你可以这样做:

  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> a List<?> ; (基本上)是 List< Object> T 可以分配给 Object 类型,编译器很高兴。



2



您的泛型方法是相同的,您传入 List< List<>> T 可以分配给 List<?> 类型并且编译器再次开心。



3



这与使用另一层嵌套的 2 基本相同。 T 仍然是 List <?> 类型。



< h2> 4

这里是一个小梨形的地方,以及我上面的点。

您的泛型方法需要 List< List< T>> 。您传入列表< List<>> 。现在,因为泛型不是协变的,所以 List<> 不能被分配给 List< T>



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

lockquote

必需: code> java.util.List< java.util.List< T>> found:
java.util.List< java.util。无法推断
类型变量 T
(参数不匹配; java.util.List< java.util.List<>> 不能转换为 java.util.List< java.util。


$ b

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



让我们再来看看这个细节:

List<?> 是一些未知类型的 List - 它可能是 List< ;整数> 列表< String> ;我们可以从 Object , 获取,但是我们不能 add 即可。

List< List<>> 是一个列表列表一些未知类型的列表 - 它可能是列表< List< Integer> > 列表< List< String>> 。在 1 的情况下,可以将 T 分配给 Object ,并且不允许在通配符列表中添加操作。在 4 的情况下,这是无法完成的 - 主要是因为没有一个泛型构造来阻止 add 到外部 List



如果编译器将 T 赋值给 Object

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

因此,由于协变性,不可能将 List<清单<整数>> 任何其他通用列表


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

... as is this...

public class Main {

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

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

... and this ...

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

... but this doesn't compile:

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;

The following will not compile:

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

1

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.

2

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.

3

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

4

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

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>.

The actual compiler error (Java 8) is:

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

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<?> 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<?>> 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.

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"));

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

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

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