泛型的概念
泛型事实上就是将类型参数化的一种方式。在我们定义一些类、方法或接口时,我们不仅可以将一些普通参数传入,而且还可以将某一类型作为参数传入。我认为,由于不知道传入的具体类型是什么,可以广泛的表示一系列的类型,所以叫做泛型。
泛型的由来
JDK5之前是没有泛型的,泛型的诞生有其意义。
List arrayList = new ArrayList();
arrayList.add("aaaa");
arrayList.add(100);
for(int i = 0; i< arrayList.size();i++){
String item = (String)arrayList.get(i);
System.out.println("泛型测试","item = " + item);
}
显然,上述程序会报错,因为arraylist中即有String类型,也有Integer类型。如果在这里能够限制一下这个列表中的元素类型,或者规定元素中必须是String类型的,那么这个程序就可以检查出有哪些不是String类型的元素了,从而可以避免上述错误,而泛型因此诞生。
现在,当我们需要一个保存String类型的元素的列表时,有
List<String> arraylist = new ArrayList<String>()
List<String> linkedlist = new LinkedList<String>()
进一步简化
List<String> arraylist = new ArrayList<>()
List<String> linkedlist = new LinkedList<>()
这里的String就作为参数传入泛型,同样的也可以有Integer,Double等
泛型的特点
List<String> l1 = new ArrayList<String>();
List<Integer> l2 = new ArrayList<Integer>();
System.out.println(l1.getClass() == l2.getClass());
上面这个例子的结果是true。为什么,明明两个列表传入的参数是不同的啊,这就要说到泛型的一个特性——类型擦除。
类型擦除
Java 的类型参数在编译器是有效的,但在运行期被删除,所有泛型参数类型在编译后都会被清除掉,并且在对象进入和离开方法的边界处添加类型检查和类型转换的方法。也就是说,泛型信息不会进入到运行时阶段。这样上面这个结果的原因就清楚了,但是为什么要这么做呢?
- 提高效率,减少麻烦,如果 JVM 将泛型类型延续到运行期,那么到运行期时 JVM 就需要进行大量的重构工作了,提高了运行期的效率。
- 有利于对之前版本的兼容,在编译期擦除可以更好地支持原生类型
具体例子
1. List<String>、List<T> 擦除后的类型为 List。
2. List<String>[]、List<T>[] 擦除后的类型为 List[]。
3. List<? extends E>、List<? super E> 擦除后的类型为 List<E>。
4. 泛型的 class 对象是相同的
5. 泛型数组初始化时不能声明泛型类型 List<String>[] list = new List<String>[]; 初始化后,由于类型擦除,List<String>[]与List<Object>[]没有两样,因此初始化的内容是否合法,运行时不能检查出来。
6. 使用instanceof时不允许存在泛型参数,和5的原因一样,看不出类型了