Effects of Type Erasure and Bridge Methods
有时类型擦除可能会导致我们无法预料的情况。下面的例子将介绍是如何产生的。例子Bridge Methods中展示了编译器有时会创建一个称为Bridge method
(桥接函数)的合成方法,这是类型擦除过程的一部分。
下面代码中有两个类:
再看下面这段代码:
特殊说明:在Orcale的文档中报错的注释写在了最后一行
Integer x = (String)mn.data; // Causes a ClassCastException to be thrown.
。然而根据实际测试情况n.setData("Hello");
就已经报错,运行JDK版本为jdk1.8.0_202
在经过类型擦除以后,上面的代码变成:
当代码运行时发现了下面这些事:
n.setData("Hello");
因为实际执行的是MyNode
的实例中的setData(Object)
函数。(MyNode
类从Node
继承了setData(Object)
)setData(Object)
函数体中,n
所引用对象中的字段data被赋值为一个String
mn
所引用的相关对象中的字段data,应该是能被访问的Integer
(因为mn
是MyNode
,也就是Node <Integer>
)。尝试将一个
String
赋值给一个Integer
会导致Java编译器在分配时插入强制转换,从而导致ClassCastException
。
Bridge Methods
当编译一个扩展参数化类或者实现参数化接口的类或者接口时,作为类型擦除过程的一部分,编译器可能需要创建一个被称为bridge method
(桥接函数)的合成函数。
通常我们不需要担心桥接函数,但是如果其中一个出现在堆栈跟踪中,可能会让人感到困惑。
在类型擦除以后,Node
和MyNode
类型变成:
经过类型擦除,函数签名不在匹配。Node
中的函数变为setData(Object)
而MyNode
中的函数变为setData(Integer)
。也就是说函数MyNode.setData
比没有重写函数Node.setData
。
为了解决该问题并确保类型擦除以后泛型类型的[多态],Java编译器生成一个桥接函数来确保子类型能像预期的一样工作。以MyNode
为例,编译器为setData
生成如下的桥接函数:
如上所见,在类型擦除以后桥接函数和Node
类中的setData
函数有着相同的函数签名,最总委托给原始的setData
函数。
Last updated