文档

Java™ 教程-Java Tutorials 中文版
类型擦除和桥接方法的影响
Trail: Learning the Java Language
Lesson: Generics (Updated)
Section: Type Erasure

类型擦除和桥接方法的影响

有时类型擦除会导致你可能没有预料到的情况。以下示例显示了如何发生这种情况。该示例(在 Bridge Methods 中描述)显示了编译器有时如何创建一个称为桥接方法(bridge method)的合成方法(synthetic method),作为类型擦除过程的一部分。

给出以下两个类:

public class Node<T> {

    public T data;

    public Node(T data) { this.data = data; }

    public void setData(T data) {
        System.out.println("Node.setData");
        this.data = data;
    }
}

public class MyNode extends Node<Integer> {
    public MyNode(Integer data) { super(data); }

    public void setData(Integer data) {
        System.out.println("MyNode.setData");
        super.setData(data);
    }
}

请考虑以下代码:

MyNode mn = new MyNode(5);
Node n = mn;            // A raw type - compiler throws an unchecked warning
n.setData("Hello");     
Integer x = mn.data;    // Causes a ClassCastException to be thrown.

类型擦除后,此代码变为:

MyNode mn = new MyNode(5);
Node n = (MyNode)mn;         // A raw type - compiler throws an unchecked warning
n.setData("Hello");
Integer x = (String)mn.data; // Causes a ClassCastException to be thrown.

以下是代码执行时发生的情况:

桥接方法

在编译继承参数化类或实现参数化接口的类或接口时,编译器可能需要创建一个合成方法,称为 bridge method (桥接方法),作为类型擦除过程的一部分。你通常不需要担心桥接方法,但如果出现在堆栈跟踪中,你可能会感到困惑。

在类型擦除之后,NodeMyNode 类变为:

public class Node {

    public Object data;

    public Node(Object data) { this.data = data; }

    public void setData(Object data) {
        System.out.println("Node.setData");
        this.data = data;
    }
}

public class MyNode extends Node {

    public MyNode(Integer data) { super(data); }

    public void setData(Integer data) {
        System.out.println("MyNode.setData");
        super.setData(data);
    }
}

在类型擦除之后,方法签名不匹配。Node 方法变为 setData(Object)MyNode 方法变为 setData(Integer)。因此,MyNode setData 方法不会覆盖 Node setData 方法。

为解决此问题并在类型擦除后保留泛型类型的 polymorphism (多态性),Java 编译器会生成一个桥接方法,以确保子类型按预期工作。对于 MyNode 类,编译器为 setData 生成以下桥接方法:

class MyNode extends Node {

    // Bridge method generated by the compiler
    //
    public void setData(Object data) {
        setData((Integer) data);
    }

    public void setData(Integer data) {
        System.out.println("MyNode.setData");
        super.setData(data);
    }

    // ...
}

正如你所看到的,在类型擦除之后,具有与 Node 类的 setData 方法相同的方法签名的桥接方法委托给原始的 setData 方法。


Previous page: Erasure of Generic Methods
Next page: Non-Reifiable Types