# 类型擦除和桥接方法的影响

有时类型擦除导致您可能没有预料到的情况。以下示例显示如何发生这种情况。 该示例(在「桥接方法」中进行了说明)显示了编译器有时会如何创建称为桥接方法的合成方法,作为类型擦除过程的一部分。

鉴于以下两类:

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);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

考虑下面的代码

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

类型擦除后,这段代码变成:

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.
1
2
3
4

这是执行代码时发生的情况:

  • n.setData("Hello") 方法在 MyNode 上执行
  • setData(Object) 的主体中,由 n 引用的对象的数据字段被分配给一个 String。
  • 可以通过 mn 引用访问 data,并且期望它是一个整数(因为 MyNode 是一个 Node <Integer>
  • 尝试强转一个字符串为 Integer,导致 ClassCastException

TIP

??? 没有看明白上面说的,擦除后怎么变成强转了

by 2019年2月20日16:23:52:现在看明白了,如果不是 idea 检测只能在运行中才能发现问题

# 桥接方法

编译扩展参数化类或实现参数化接口的类或接口时,编译器可能需要创建一个称为桥接方法的合成方法, 作为类型擦除过程的一部分。您通常不需要担心桥接方法,但是如果出现在堆栈轨迹中,您可能会感到困惑。

类型擦除后,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);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

类型擦除后,方法签名不匹配。Node 的 setData(Object data) 和 MyNode 的 setData(Integer data) 方法不会被重写了。

为了解决这个问题并在类型擦除之后保留泛型类型的多态性,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);
    }

    // ...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

正如你看到的,桥接方法具有和 Node 类方法签名一致的方法,然后委托具体的类型方法。