第一次看到《java核心技术卷一》中关于泛型这部分的时候感觉很复杂,似乎有说不完的约束条件,让人难以理解。当时只是囫囵吞枣般过了一遍,也没有看出个什么来。现在是时候回过头来认真学习学习这方面的知识啦。在这里记录一下JAVA泛型中比较难理解的部分。
泛型表达式的类型擦除
按照书中的例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
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>
如果类型限定是多个接是多个接口,如果下面的例子:
1
2
3
4
5
6
7
8
9
|
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)放在边界列表的末尾。
作者还提到编译在处理(翻译)泛型表达式的时候的一些细节问题:
当程序调用泛型方法时,如果擦除返回类型,编译器插入强制类型转换。例如:
1
2
|
Pair<Employee> buddies=...;
Employee buddy=buddies.getFirst();
|
擦除类型之后将返回Object类型(假设类型T是没有限定的),编译器自动插入Employee的强制类型转换,也就是说,编译器把这个方法的调用翻译成了两条虚拟机指令:
1
|
Employee buddy=(Employee)buddies.getFirst();
|
1。对原始方法(也就是擦除之后的)Pair.getFirst()的调用,这个方法返回一个Object对象。
2。将返回的Object类型的对象强制转换为Employee。
通配符:
直观地讲,带有超类型限定的通配符声明的对象,可以对其进行写入操作。
而带有子类型限定的通配符声明的对象,可以对其进行读取操作。