Java 教程是为 JDK 8 编写的。本页中描述的示例和实践未利用在后续版本中引入的改进。
有时类型擦除会导致你可能没有预料到的情况。以下示例显示了如何发生这种情况。该示例(在 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 (桥接方法),作为类型擦除过程的一部分。你通常不需要担心桥接方法,但如果出现在堆栈跟踪中,你可能会感到困惑。
在类型擦除之后,Node 和 MyNode 类变为:
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 方法。