首页 > 代码库 > 泛型学习笔记
泛型学习笔记
定义:参数化类型。使程序具有更好的可读性和安全性。
优点:
(泛型类,比如用于集合等容器类型时)
1.编译器进行类型检查,避免插入错误类型的对象;
2.输出时不需要类型的强制转换。
1.泛型类
可以有多个类型变量。
例:
public class Pair<T>{...}
public class Pair<K,V>{...}
2.泛型方法
既可以定义在普通类中,也可以定义在泛型类中。
例:
public static <T> T getMiddle(T[] a){
return a[a.length/2];
}
调用时,可省略<T>类型参数。
3.类型变量的限定
实现方法:<T extends BoundingType>
表示T使绑定类型的子类型。其中,T和BoundingType可以是类,也可以是接口。且绑定类型可以有多个,中间用&连接,如<T extends Comparable & Serializable>。因为Java只能单继承,因此限定类型中至多只能有一个类,并且必须位于限定列表中的第一个。
4.泛型代码和虚拟机
虚拟机没有泛型类型对象——所有对象都属于普通类。即编译器生成的字节码是不包含泛型信息的,泛型类型信息在编译处理时被擦出——类型擦除。
类型擦除:
--定义:通过类型参数和并,将泛型类型实例关联到一份字节码上。
--原因:Java为引用类型系统,引用类型集合中的元素本质上都是一个个指针,没必要为每个类型都产生一份执行代码(浪费空间)。
--实现方法:
(1)将所有的泛型参数用最左边界(最顶级的父类型)类型替换;
(2)移除所有类型参数。
4.1 翻译泛型表达式
当程序调用泛型方法时,如果擦除返回类型,编译器将进行强制类型转换。
4.2 翻译泛型方法
例:
class DateInterval extends Pair<Date>{
public void getSecond(Date second){
if(second.compareTo(getFirst())>=0)
super.setSecond(second);
}
}
类型擦除后,变成
class DateInterval extends Pair{
public void getSecond(Date second){
if(second.compareTo(getFirst())>=0)
super.setSecond(second);
}
}
那么,DateInterval类中存在一个从Pair类中继承的方法:public void getSecond(Object date){...}。存在问题。
考虑:
DateInterval interval=new DateInterval();
Pair<Date> pair=interval;
pair.setSecond(aDate);
希望对于setSecond的调用具有多态性,因为pair引用DateInterval的对象,故应调用DateInterval中的setSecond方法。问题在于类型擦除与多态发生了冲突。因此,编译器会在DateInterval类中生成一个桥方法(用来保持多态):
public void setSecond(Object aDate){
setSecond((Date) aDate);
}
5.Java泛型的约束与局限性
(1)不能用基本类型实例化参数类型
例:Pair<double>//error;应为Pair<Double>
(2)运行时类型查询只适用于原始类型
例:
A)if(a instanceof Pair<T>)//error;只能测试a是否是任意类型的Pair
B)Pair<String> p=(Pair<String>) a//warning; 只能测试a是否是任意类型的Pair
C)getClass也总是返回原始类型
Pair<String> sPair=...
Pair<Employee> ePair=...
if(sPair.getClass()==ePair.getClass())//true
(3)不能创建参数化类型的数组
不能创建,如Pair<String>[] pArr=new Pair<String>[10];//error
但是可以声明,如Pair<String>[] pArr;
一种安全有效地方法是使用ArrayList,即ArrayList<Pair<String>>。
(4)不能实例化类型变量
(5)泛型类的静态上下文中类型变量无效
(6)不能抛出或捕获泛型类的实例
(7)注意擦除后的冲突
6.泛型类型的继承规则
注意泛型与数组的区别。
例如:
一个ArrayList<Manager>可转换为一个List<Manager>;
但是,一个ArrayList<Manager>不能转换为ArrayList<Employee>或List<Employee>。
7.通配符类型
7.1 子类型限定
Pair<? extends Employee>
? extends Employee getFirst()//合法,可赋值给Manager对象
void setFirst(? extends Employee)//不合法,编译器不能判断具体是什么类型
7.2 超类型限定
Pair<? super Manager>
? super Manager getFirst()//不合法,只能赋值给一个Object类型对象
void setFirst(? super Manager)//合法,编译器不知道具体类型,但是可以用任何Manager对象或子类型对象调用,但不能用Employee对象
小结:带有超类型限定通配符的可以向泛型对象写入,带有子类型限定通配符的可以从泛型对象读出。
7.3 无限定通配符
Pair<?>
? getFirst()//只能赋值给一个Object
void setFirst(?)//不能被调用,甚至不能被Object调用
Pair<?>与Pair的本质不同在于:可以用任意Object对象调用原始Pair类的setFirst方法。
泛型学习笔记