我正在尝试理解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) { }
}
有人可以用简单的语言解释发生了什么吗?
答案 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
所以,逐个浏览你的例子
您的通用方法需要List<T>
,您传入List<?>
;它(基本上)是List<Object>
。可以将T
分配给Object
类型,编译器也很满意。
您的通用方法是相同的,您传入List<List<?>>
。可以将T
分配给List<?>
类型,编译器也很高兴。
这与其他嵌套级别的 2 基本相同。 T
仍然是List<?>
类型。
这是一个小梨形的地方,以及我从上面的观点出现的地方。
您的通用方法需要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}} - 可能是List
或List<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>>
。