Java教程已针对JDK 8进行编写。本页面中描述的示例和实践不利用后续版本中引入的改进,并可能使用不再可用的技术。
有关Java SE 9及后续版本中更新的语言功能的摘要,请参阅Java语言更改。
有关所有JDK版本的新功能、增强功能以及已删除或不推荐使用的选项的信息,请参阅JDK发行说明。
类型擦除和桥方法的影响
{ 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
{ public MyNode(Integer data) { super(data); } public void setData(Integer data) { System.out.println("MyNode.setData"); super.setData(data); } } ``` 考虑以下代码: ```java MyNode mn = new MyNode(5); Node n = mn; // 一个原始类型 - 编译器会抛出一个未检查的警告 n.setData("Hello"); // 导致抛出ClassCastException异常。 Integer x = mn.data; ``` 在类型擦除后,该代码变为: ```java MyNode mn = new MyNode(5); Node n = mn; // 一个原始类型 - 编译器会抛出一个未检查的警告 // 注意:这个语句也可以写成以下形式: // Node n = (Node)mn; // 然而,编译器不会生成一个强制转换,因为它不是必需的。 n.setData("Hello"); // 导致抛出ClassCastException异常。 Integer x = (Integer)mn.data; ``` 下一节解释了为什么在`n.setData("Hello");`语句中会抛出`ClassCastException`。 桥接方法 当编译一个继承参数化类或实现参数化接口的类或接口时,编译器可能需要在类型擦除过程中创建一个合成方法,称为桥接方法。通常情况下,您不需要担心桥接方法,但如果在堆栈跟踪中出现一个,您可能会感到困惑。 在类型擦除后,`Node`和`MyNode`类变为: ```java 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(T)`方法变为`Node.setData(Object)`。因此,`MyNode.setData(Integer)`方法不覆盖`Node.setData(Object)`方法。 为了解决这个问题并在类型擦除后保留泛型类型的多态性,Java编译器生成桥接方法以确保子类型工作正常。
对于MyNode类,编译器为setData生成了以下桥接方法:
class MyNode extends Node {
// 编译器生成的桥接方法
//
public void setData(Object data) {
setData((Integer) data);
}
public void setData(Integer data) {
System.out.println("MyNode.setData");
super.setData(data);
}
// ...
}
桥接方法MyNode.setData(object)
委托给原始的MyNode.setData(Integer)
方法。因此,n.setData("Hello");
语句调用的是MyNode.setData(Object)
方法,会抛出ClassCastException
,因为"Hello"
无法转换为Integer
。