有时类型擦除会导致您可能无法预料的情况。以下示例显示了这种情况的发生方式。该示例(在Bridge方法中进行了描述 )显示了编译器有时如何创建合成方法(synthetic method),称为桥接方法,作为类型擦除过程的一部分。
给定以下两类:

  1. public class Node<T> {
  2. public T data;
  3. public Node(T data) { this.data = data; }
  4. public void setData(T data) {
  5. System.out.println("Node.setData");
  6. this.data = data;
  7. }
  8. }
  9. public class MyNode extends Node<Integer> {
  10. public MyNode(Integer data) { super(data); }
  11. public void setData(Integer data) {
  12. System.out.println("MyNode.setData");
  13. super.setData(data);
  14. }
  15. }

考虑以下代码:

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

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

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

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

  • n.setData(“ Hello”); 使方法setData(Object)在类MyNode的对象上执行。(MyNode类从Node继承了setData(Object)。)
  • 在setData(Object)的主体中,将n引用的对象的数据字段分配一个字符串。
  • 可以访问通过mn引用的同一对象的数据字段,并且该数据字段应为整型(因为mn是MyNode,它是Node
  • 尝试将字符串分配给Integer,会导致Java编译器在分配时插入的强制转换,进而导致ClassCastException。

    桥接方法

    在编译扩展参数化类或实现参数化接口的类或接口时,作为类型擦除过程的一部分,编译器可能需要创建一个称为桥接方法(bridge method的综合方法。您通常不必担心桥接方法,但是如果其中一个出现在堆栈跟踪中,您可能会感到困惑。
    类型擦除后,Node和MyNode类变为:
  1. public class Node {
  2. public Object data;
  3. public Node(Object data) { this.data = data; }
  4. public void setData(Object data) {
  5. System.out.println("Node.setData");
  6. this.data = data;
  7. }
  8. }
  9. public class MyNode extends Node {
  10. public MyNode(Integer data) { super(data); }
  11. public void setData(Integer data) {
  12. System.out.println("MyNode.setData");
  13. super.setData(data);
  14. }
  15. }

类型擦除后,方法签名不匹配。 Node方法变为setData(Object),而MyNode方法变为setData(Integer)。 因此,MyNode setData方法不会重写Node setData方法。
为了解决此问题并在类型擦除后保留泛型类型的 多态性,Java编译器生成了一个桥接方法来确保子类型能够按预期工作。对于MyNode类,编译器为setData生成以下桥接方法:

  1. class MyNode extends Node {
  2. // Bridge method generated by the compiler
  3. //
  4. public void setData(Object data) {
  5. setData((Integer) data);
  6. }
  7. public void setData(Integer data) {
  8. System.out.println("MyNode.setData");
  9. super.setData(data);
  10. }
  11. // ...
  12. }

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