泛型,类型参数和通配符 [英] Generics, Type Parameters and Wildcards
问题描述
我试图理解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
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 tojava.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屋!