泛型是JDK1.5时添加的,为了向前兼容,同时不对JVM进行大改,泛型在编译和运行期会擦除类型,这就会导致一些问题。
以数组为例:
String[] s = String[5];
Object[] o = s;
o.add(Object());
我们看这段代码,String数组是不能够添加Obect类型的对象的,运行时数组的具体类型是知道的,所以运行时会报错,编译前检查时不报错。
再来看泛型集合:
List<String> s = ArrayList<>();
List<Object> o = s;
o.add(Object());
由于类型擦除,运行期间并不知道o的具体类型,所以无法报错,只能放到编译前检查时报错。
java中是没有完全的逆变、协变的,List<String>不是List<Object>的子类型,为了解决这个问题,就引入了通配符的概念。
extends只能get,不能set:
List<String> s = ArrayList<>();
Object f(List<? extends Object> o) {
o.add("ss"); //编译前检查会报错,由于o的具体类型不知道,只知道是Object的某个子类,不一定是String,所以添加string会报错。
return o.get(0); //Object类型
}
f(s);
super只能set,不能get:
List<Object> o = ArrayList<>();
void f(List<? super String> s) {
s.add("ss"); //正确。
s.get(0); //没问题
String temp = s.get(0); // 编译前检查会报错,类型不对
}
f(s);
总结:
无论是数组还是集合,都不能随便使用,无非是编译前还是运行时报错的问题。