List<?> 和 List<Object> 的区别
区别:
List<?>变量的元素只能读,不能写(可以插入null元素)List<Object>不是List<String>的父类,但List<?>是所有List泛型的父类,因此List<?>变量可以被赋值为多种List泛型类型
public static void printList(List<Object> list) {
for (Object elem : list)
System.out.println(elem + " ");
System.out.println();
}
上面代码中定义的方法只能接受List<Object>类型的变量为参数,不能接受List<String>,改成List<?>的话就可以接受了。
List<?> 和 List 的区别
List<?>是所有List泛型的父类,而List是List泛型的原类型(raw type)。允许使用List原类型是为了兼容引入泛型前的代码,使用原类型会失去泛型的安全性和表达性,可能在运行时报错,还需要自己转型。因此编译器会给出警告。
注意,有一些需要使用原类型的场景:
- 在类的字面量中必须使用原类型,
List.class、String[].class和int.class是正确的,但List<?>.classList<String>.class是错误的。 - instanceof 操作符中使用原类型就够了,因为泛型信息会在运行时擦除,在 instanceof 操作符中只能使用无界通配符类型或原类型,而且两者并没有区别,推荐后者。
// instanceof 操作符的推荐使用方式
if (o instanceof Set) {
Set<?> s = (Set<?>) o; // 这种转型时有检查的,不会有编译时警告
}
<?> 的使用场景
- 用来读取的List变量(in),使用extend定义下界;如果最多只调用Object类的方法,就改用无界通配符
<?> - 用来写入的List变量(out),使用super定义上界
- 当List变量同时需要in-out访问时,不使用通配符
例如JDK源码中List接口的的containsAll、removeAll方法都是用<?>定义的:
boolean containsAll(Collection<?> c);
boolean removeAll(Collection<?> c);
而addAll方法,虽然也是读取,但是在后续使用时,会调用E的方法,因此不能使用<Object>通配符。
boolean addAll(Collection<? extends E> c);
参考备注
- 参考了Oracle Java文档 Unbounded Wildcards 和 Guidelines for Wildcard Use