为什么用泛型程序设计
类型参数好处
java增加泛型类之前,泛型程序设计用继承实现,ArrayList类只维护一个Object引用的数组
这就产生两个问题:
当获取一个值时必须进行强制转换
没有错误检查,可以向数组列表添加任何类的值
泛型提供一个更好的解决方案:类型参数<>
这使得代码更好的可读性,人们看就知道这个数组列表包含的String对象
出现编译错误比运行出现类的强制类型转换异常好的多
类型参数(泛型)的魅力:让你的程序更易读就,更安全
谁想成为泛型程序员
放行程序设计基本水平:仅仅会使用泛型,不考虑它如何工作以及为什么这样做
如果代码原本设计大量通用类型的强制转换,只有这些代码才会使用类型参数而收益
定义简单泛型类
public class Demo01<S> { }
泛型方法
public static <String> void aVoid()
类型变量在修饰符后面,在返回值前面
泛型方法可以在普通类中定义,也可以在泛型类定义
类型变量和限定
<T extends BoundingType>
<T extends BoundingType & Serializable>
T 应该是限定类型的子类型,T和限定类型可以是类,也可以是接口,选择关键字extends的原因是他更接近子类型概念
public static <T extends Comparable>Pair<T>minmax(T[] a)
泛型代码和虚拟机
虚拟机没有泛型类型对象,所有对象都属于普通类
类型擦除
原始类型(Object)名字是去掉类型参数后的泛型类型名,类型变量会被擦除,替换为限定类型
转换泛型表达式
编写一个泛型方法调用,如果擦除了返回类型,编译器插入强制类型转换
编译器把这个方法调用转换为两条虚拟机命令:
- 对原始方法Pair.getFirst调用
- 将返回的Object类型强制转换为Employee类型
转换泛型方法
- 虚拟机没有泛型,只有普通的类和方法
- 所有的类型参数都会替换为他们的限定类型
- 会合成桥方法来保持多态
- 为保持类型安全性,必要时会插入强制类型转换
限制与局限性
不能用基本类型实例化类型参数
不能用基本类型代替类型参数,原因在于类型擦除
Object不能存储double值,但可以接受Double包装类
运行时类型查询只适用于原始类型
虚拟机对象总有一个特定的非泛型类型
if(a instanceof Pair<T>)
Pair <String> p = (Pair <String>) a; //error
不能创建参数化类型的数组
new Pair<String> [10];//error
Varargs(可变参数)警告
public static <T> void addAll(Collection<T> coll,T...ts){
for(T t : ts) coll.add(t);
}
不能实例化类型变量
new T(); //error
不能构造泛型数组
new T[2]; //error
泛型类的静态上下文中类型变量无效
不能在静态字段或方法中引用类型变量
private static T singleInstance; //error
不能抛出或捕获泛型类
public class p<T> extends Exception{} //error
可以取消对检查型异常的检查
必须为所有检查型异常提供一个处理器,不过可以利用泛型取消这个机制
Task.<RuntimeException>throwAs(e);
注意擦除后的冲突
当泛型类型被擦除,不允许创建引发冲突的条件
假若两个接口类型时同一个接口的不同参数化,一个类或类型变量就不能同时作为这两个接口类型的子类
class Employee implements Comparable<Employee>{}
class Manager extends Employee implements Comparable<Manager>{} //error
泛型类型的继承规则
Manager是Employee子类,Pair不是Pair子类
通配符类型
Pair <? extends Employee> //Employee的子类
? super Manager //Manager的父类