类型消除和 bridge 方法的影响

有时类型擦除会导致您可能无法预料的情况。以下示例显示了这种情况的发生方式。该示例(在Bridge Methods中描述)显示了编译器有时如何创建综合方法(称为 bridge 方法)作为类型擦除过程的一部分。

给定以下两类:

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.

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

  • n\.setData\("Hello"\);使方法setData\(Object\)在类MyNode的对象上执行。 (MyNode类从Node继承了setData\(Object\).)

  • setData\(Object\)的主体中,由n引用的对象的数据字段分配给String

  • 可以访问通过mn引用的同一对象的数据字段,并且该字段应为整数(因为mnMyNode,而Node\<Integer\>

  • try将String分配给Integer会导致 Java 编译器在分配时插入的强制转换产生ClassCastException

Bridge Methods

在编译扩展了参数化类的类或interface或实现参数化interface的类时,作为类型擦除过程的一部分,编译器可能需要创建一个称为* bridge method *的综合方法。您通常不必担心 bridge 方法,但是如果其中一个出现在堆栈跟踪中,您可能会感到困惑。

类型擦除后,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 编译器生成了一个 bridge 方法以确保子类型能够按预期工作。对于MyNode类,编译器为setData生成以下 bridge 方法:

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);
    }

    // ...
}

如您所见,bridge 方法(在类型擦除后与Node类的setData方法具有相同的方法签名)将委托给原始的setData方法。