文档

Java™教程
隐藏目录
类型擦除和桥方法的影响
路径:学习Java语言
课程:泛型 (已更新)
章节:类型擦除

类型擦除和桥方法的影响

{ 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


上一页: 泛型方法的擦除
下一页: 非可重构类型