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 方法。