java泛型程序设计——翻译泛型表达式+翻译泛型方法
2015-12-07 19:05
555 查看
【0】README
0.1) 本文描述+源代码均 转自 core java volume 1, 旨在理解 java泛型程序设计 的 翻译泛型表达式+翻译泛型方法 的知识;【1】翻译泛型表达式
1.1)当程序调用泛型方法时, 如果擦除了泛型返回类型, 编译器插入类型转换;1.1.1)看个荔枝:
Pair<Employee> buddies = ... Employee buddy = buddies.getFirst();
擦除getFirst的返回类型后将返回Object类型。 编译器自动插入 Employee 的强制类型转换。
也就是说, 编译器吧这个方法调用翻译为两条虚拟机指令(Commands):
C1)对原始方法 Pair.getFirst 的调用;
C2)将返回的Object类型 强制转换为 Employee 类型;
1.2)当存取一个泛型域时也要插入强制类型转换。
1.2.1)假设 Pair 类的first 域 和 second 域都是 公有的(这不是种好的编程风格, 但在java语法中,这是合法的)。
表达式: Employee buddy = buddies.first; 也会在结果字节码中插入强制类型转换;
【2】翻译泛型方法
2.1)类型擦除也会出现在泛型方法中。public static <T extends Comparable> T min(T[] a):是一个完整的方法族;
2.1.1)擦除类型后, 只剩下一个方法:
public static Comparable min(Comparable[] a)
注意, 类型参数T 已经被擦除了, 只留下了限定类型 Comparable;
2.2)方法擦除带来了两个复杂问题。
2.2.1)看个荔枝:
class DateInterval extends Pair<Date> { public void setSecond(Date second) { if(second.compareTo(getFirst()) >= 0) super.setSecond(second); } }
对以上代码的分析(Analysis):
A1)上述类的类型变量擦除后, 为
class DateInterval extends Pair // after erasure { public void setSecond(Date second) { if(second.compareTo(getFirst()) >=0) super.setSecond(second); } }
A2)令人感到奇怪的是, 存在另一个从Pair 继承的setSecond方法, 即
public void setSecond(Object second)
Attention)此时要注意, DateInterval内部应该是重写了 Pair的 setSecond方法, 结果擦除类型参数后, 就不是重写了, 破坏了类的多态性;
A3)它们显然不是同一种方法, 因为有不同的类型参数, 一个是Object , 而另一个是 Date;(这里是干货)
A4)然而不应该不一样, 考虑下面的语句序列:
DateInterval interval = new DateInterval(); Pair<Date> pair = interval; // OK--assignment to superclass pair.setSecond(aDate); // "那这条语句调用哪个 setSecond方法呢? 是 setSecond(Object) 还是 setSecond(Date) 呢?"
A5)这里, 希望对setSecond 的调用 具有多态性, 并调用最合适的那个方法。 由于pair 引用DateInterval 对象,所以应该调用 DateInterval.setSecond;
A6)出现的问题:在于类型擦除与多态发生了冲突, 确实, 如上面的Attention所说, 变量类型擦除破坏了类的多态性;
A7)解决方法: 就需要编译器在 DateInterval 类中生成一个桥方法(bridge method):
public void setSecond(Object second) //这里就调用了 重写的 父类 DateInterval(Object) {setSecond((Date)second)}
2.3)上述引入了桥方法:要想了解他的工作过程, 跟踪下列语句 pair.setSecond(aDate);
2.3.1)变量pair 已经说明为类型 Pair , 并且这个类型只有一个简单的方法叫做 setSecond, 即 setSecond(Object);
2.3.2)虚拟机用 pair 引用的对象调用这个方法: 这个对象是 DateInterval 类型的, 因而将会调用 DateInterval.setSecond(Object) 方法;
2.3.3)这个方法是合成的桥方法: 它调用 DateInterval.setSecond(Date)方法,在正是我们想要的;
2.4)桥方法也可以变得很奇怪, 如 DateInterval 方法覆盖了 getSecond()方法:
class DateInterval extends Pair<ate> { public Date getSecond() { return (Date) super.getSecond().clone(); } }
对以上代码的分析(Analysis):
A1)在擦除的过程中, 有两个getSecond方法:
A1.1) Date getSecond() // defined in DateInterval
A1.2) Object getSecond() // overrides the method defined in Pair to call the first method
A2)不能这样编写代码(因为具有相同参数的两个方法是不合法的, 他们都没有参数)。
A3)但在虚拟机中, 用参数类型和返回类型确定一个方法, 因此, 编译器可能产生两个仅返回 类型不同的 方法字节码, 虚拟机能够正确处理这个情况;
Annotation)
A1)桥方法不仅用于泛型类型; 还有, 在一个方法覆盖另一个方法时可以指定一个更严格的返回类型:
public class Employee implements Clonealbe { public Employee clone() throws CloneNotSupportedException() {} }
对以上代码的分析(Analysis):
A1) Object.clone 和 Employee.clone 方法被说成具有协变的返回类型;(具有协变的返回类型)
A2)实际上, Employee 类有两个克隆方法(Methods):
M1) Employee clone();// defined above
M2) Object clone() ; // synthesized bridge method, overrides Object.clone;
A3)合成的桥方法调用了新定义的方法;
Conclusion)总之, 需要记住有关java泛型转换的事实:
C1)虚拟机中没有泛型, 只有普通的类和方法;
C2)所有的类型参数都是用它们的限定类型替换;
C3)桥方法被合成来保持多态;
C4)为保持类型安全性, 必要时插入强制类型转换;
相关文章推荐
- java对世界各个时区(TimeZone)的通用转换处理方法(转载)
- java-注解annotation
- java-模拟tomcat服务器
- java-用HttpURLConnection发送Http请求.
- java-WEB中的监听器Lisener
- Android IPC进程间通讯机制
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- 介绍一款信息管理系统的开源框架---jeecg
- 聚类算法之kmeans算法java版本
- java实现 PageRank算法
- PropertyChangeListener简单理解
- 插入排序
- 冒泡排序
- 堆排序
- 快速排序
- 二叉查找树
- [原创]java局域网聊天系统