先发布,以后有空再修改。。。
第一次看到《java核心技术卷一》中关于泛型这部分的时候感觉很复杂,似乎有说不完的约束条件,让人难以理解。当时只是囫囵吞枣般过了一遍,也没有看出个什么来。现在是时候回过头来认真学习学习这方面的知识啦。在这里记录一下JAVA泛型中比较难理解的部分。
泛型表达式的类型擦除
按照书中的例子:
class Pair<T>{
public Pair(){
first=null;
second=null;
}
public Pair(T _first,T _second){
first=_first;
second=_second;
}
public void setFirst(T _first){
first=_first;
}
public void setSecond(T _second){
second=_second;
}
public T getFirst(){
return first;
}
public T getSecond(){
return second; }
public String toString(){
return "First:" +first + "\tSecond:"+second; }
private T first;
private T second;
}
书中提到:虚拟机中是没有泛型类型对象——所有对象都属于普通类。也就是说经过编译之后的字节码文件中是没有“泛型”这个概念的。
因为T是一个无限定的变量,所以上面的代码中的类型T,可以直接用Object来代替,Object是所有类的超类。无论是Pair<String>或者Pair<Integer>,编译后,它们都将变成Pair<Object>
如果类型限定是多个接是多个接口,如果下面的例子:
public class Interval<T extends Comparable & Serializable>{
public Interval(T first,T second){
if(first.compareTo(second)<=0){lower=first;upper=second;}
else {lower=second;upper=first;}
}
...
private T lower;
private T upper;
}
那么编译器在编译的时候,是按接口出现的顺序来进行替换的。如上面,类型T将会被用Comparable来替换。如果要用到类型的Serializable接口的时候,编译会在对应的地方加入强制转换语句。同样,如果限定为: T extends Serializable & Comparable的话,类型T将会被用Serializable来替换。因此,作者说了,为了提高效率,应该将标签接口(没有方法的接口,比如Cloneable)放在边界列表的末尾。
作者还提到编译在处理(翻译)泛型表达式的时候的一些细节问题:
当程序调用泛型方法时,如果擦除返回类型,编译器插入强制类型转换。例如:
Pair<Employee> buddies=...;
Employee buddy=buddies.getFirst();
擦除类型之后将返回Object类型(假设类型T是没有限定的),编译器自动插入Employee的强制类型转换,也就是说,编译器把这个方法的调用翻译成了两条虚拟机指令:
Employee buddy=(Employee)buddies.getFirst();
1。对原始方法(也就是擦除之后的)Pair.getFirst()的调用,这个方法返回一个Object对象。
2。将返回的Object类型的对象强制转换为Employee。
通配符:
直观地讲,带有超类型限定的通配符声明的对象,可以对其进行写入操作。
而带有子类型限定的通配符声明的对象,可以对其进行读取操作。