泛型与集合的联合使用,可以把泛型的功能发挥到极致.
public class ListNoGeneric {
public static void main(String[] args) {
// 第一段: 泛型出现之前的集合定义方式
List a1 = new ArrayList();
a1.add(new Object());
a1.add(new Integer(111));
a1.add(new String("hello a1"));
// 第二阶段: 把a1引用赋值给a2,注意a2与a1的区别是增加了泛型限制
List<Object> a2 = a1;
a2.add(new Object());
a2.add(new Integer(222));
a2.add(new String("hello a2"));
// 第三段: 把a1 引用赋值给a3,注意a3与a1的区别是增加了泛型
List<Integer> a3 = a1;
a3.add(new Integer(333));
// 下方两行编译错误,不允许增加费Integer类型进入集合
// a3.add(new Object());
// a3.add(new String("hello a3"));
// 第四段: 把a1引用赋值给a4,a1与a4 的区别是增加了通配符
List<?> a4 = a1;
// 允许删除和清除元素
a1.remove(0);
a4.clear();
// 编译出错,不允许增加任何元素
// a4.add(new Object());
}
}
第一段说明: 在定义List之后,毫不犹豫的往集合里装入三种不同的对象: Object, Integer和String, 遍历没有问题,但是贸然以为里面的元素都是Integer,使用强制转换,则抛出 ClassCastException 异常.
第二段说明: 把a1 赋值给a2, a2 是List 类型的,也可以再往里装入三种不同的对象. 但是如果在第三段中 List
是会出现编译错误,所有List和List 是不完全相同的.
第三段说明: 由于JDK5之后才出现泛型,考虑到向前兼容,因此历史代码有时需要赋值给新泛型代码,从编译器角度是允许的.
下面是一段问题代码:
JSONObject jsonObject = JSONObject.formObject("{\"level\":[\"3\"]}");
List<Integer> intList = new ArrayList<>(10);
if(jsonObject != null){
intList.addAll(jsonObject.getJSONArray("level"));
int amount = 0;
for(Integer t : intList){
// 抛出 ClassCaseException 异常 : String cannot be cast to Integer
if(condition){
amount = amount + t;
}
}
}
addAll 的定义如下:
public boolean addAll(Collection<? extends E> c){
...}
进行了泛型限制,示例中addAll 的实际参数时 getJSONArray 返回的 JSONArray 对象,它并非为List,更加不是Integer 集合的子类,但为何编译不报错?查看JSONArray 的定义:
pubic final class JSONArray extends AbstractJSON implements JSON,List{
}
JSONArray 实现了List, 是非泛型集合,可以赋值给任何泛型限制的集合. 编译可以通过,但在运行时报错,这个一个隐藏得很深的Bug,最终导致发生线上故障. 在JDK5之后,应尽量使用泛型定义, 以及使用类, 集合, 参数等.
List 赋值给 List 是不允许的,若反过来赋值:
List<Integer> intList = new ArrayList<>(3);
intList.add(111);
List<Object> objects = intList;
事实上,依然会编译错误.
注意: 数组可以这样赋值,因为它是协变的,而集合不是.
第四段说明: 问号在正则表达式可以匹配任何字符,List> 称为通配符集合. 它可以接受任何类型的集合引用赋值,不能添加任何元素, 但是可以remove 和 clear 操作, 并非 immutable 集合. List> 一般作为参数来接收外部的集合,或者返回一个不知道具体类型的集合.
List 最大的问题是只能放置一种类型,如果随意转换类型的话,就是破窗理论, 泛型就失去了类型的安全意义.
** extends T> 与 super T>**两种语法:
extends T> 是Get First , 适用于,消费集合元素为主的场景; super T> 是 Put First , 适用于,生产集合元素为主的场景.