通过一个例子来比较这几种写法的区别
public class ListWithoutGeneric {
public static void main(String[] args) {
// 第一段:不使用泛型的方式
List a1 = new ArrayList();
a1.add(new Object());
a1.add(new Integer(1));
a1.add(new String("a1"));
// 第二段:把a1赋值给List<Object>类型的a2,看似a2与a1没有区别
List<Object> a2 = a1;
a2.add(new Object());
a2.add(new Integer(2));
a2.add(new String("a2"));
// 但是如果尝试把一个带有其它类型泛型的b2赋值给a2,则会编译报错
List<String> b2 = new ArrayList<>();
// 编译报错,这也是List与List<Object>的区别
a2 = b2;
// 第三段:把a1赋值给List<Integer>类型的a3,赋值过程没有编译报错,主要为了向前兼容(泛型jdk1.5之后才出现)
List<Integer> a3 = a1;
a3.add(new Integer(3));
// java.lang.ClassCastException: java.lang.Object cannot be cast to java.lang.Integer
Integer integer = a3.get(0);
// 编译报错,不允许add非Integer类型
a3.add(new Object());
a3.add(new String("a3"));
// 第四段:把a1赋值给List<?>类型的a4
List<?> a4 = a1;
a4.remove(0);
a4.clear();
a4.add(null);
// 编译出错,不允许add非null的数据
a4.add(new Object());
}
}
1、第一段:定义了一个没使用泛型的List a1,向a1中添加不同类型的元素,所有功能都可以正常使用,但使用时会有类型强制转换异常的风险。
2、第二段:把a1赋值给List<Object>类型的a2,再往a2添加不同类型元素,不会编译报错,看似a2与a1没有区别,但是如果尝试把一个带有其它类型泛型(List<String>)的b2赋值给a2,则会编译报错,除非让泛型保持一致。
这也是List与List<Object>的区别:泛型之间只有同类型才能相互赋值。
3、第三段:把a1赋值给List<Integer>类型的a3,赋值过程没有编译报错,主要为了向前兼容(泛型jdk1.5之后才出现),但如果直接用Integer类型取值,会报类型转换异常。因为a3有了泛型约束,再添加其它类型元素,则会编译报错。
4、List<?>是通配符集合,一般作为参数来接收外部的集合,或者返回一个不知道具体元素类型的集合。它可以被任何类型的集合引用赋值,也可以删除元素。但是因为类型的不确定,所有不能添加非null元素(null属于任何类型)。
以上内容根据《码出高效Java开发手册》学习整理