java泛型程序设计——翻译泛型表达式+翻译泛型方法

【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)

java泛型程序设计——翻译泛型表达式+翻译泛型方法_第1张图片

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