您的位置:首页 > 编程语言 > Java开发

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 泛型 翻译泛型