在我上一篇博文中,我介绍了一种创建策略类的方法,该方法充分利用了应用程序中存在的任何泛型元数据。在那篇文章的末尾,我展示了这段代码片段
EntitlementCalculator calculator = new DividendEntitlementCalculator();
calculator.calculateEntitlement(new MergerCorporateActionEvent());
你会记得DividendEntitlementCalculator被定义为
public class DividendEntitlementCalculator implements EntitlementCalculator<DividendCorporateActionEvent> {
public void calculateEntitlement(DividendCorporateActionEvent event) {
}
}
因此,将MergerCorporateActionEvent的实例传递给calculateEntitlement方法是不正确的DividendEntitlementCalculator类。然而,正如我在上一篇博文中所说,这段代码可以编译。为什么?因为EntitlementCalculator.calculateEntitlement()被定义为接受任何继承自CorporateActionEvent的类型,所以它应该能够编译。那么在这种情况下,运行时会发生什么,Java 是如何强制执行类型安全的呢?正如你可能想到的,运行这段代码会得到一个ClassCastException,表示你无法将MergerCorporateActionEvent转换为DividendCoporateActionEvent。通过这种方式,Java 可以为你的应用程序强制执行类型安全——不可能将MergerCorporateActionEvent潜入预期接受DividendCorporateActionEvent的方法中。
这里真正的问题是:“那个ClassCastException来自哪里?”答案很简单——Java 编译器通过引入一个桥接方法(bridge method),适当地添加了创建并抛出异常的代码。桥接方法是编译器生成的合成方法,会添加到你的类中,以确保在处理泛型类型时的类型安全。
在上面所示的例子中EntitlementCalculator.calculateEntitlement可以接受与CorporateActionEvent类型兼容的任何对象进行调用。然而,DividendEntitlementCalculator只接受与DividendCorporateActionEvent类型兼容的对象,但是,由于你可以通过DividendEntitlementCalculator接口调用EntitlementCalculator,它也必须接受CorporateActionEvent。那么这在编译后的类文件中会变成什么样呢?我们有用户提供的方法
public void calculateEntitlement(DividendCorporateActionEvent event) {
System.out.println(event);
}
这会转化为这段字节码
public void calculateEntitlement(bigbank.DividendCorporateActionEvent);
Code:
Stack=2, Locals=2, Args_size=2
0: getstatic #2; //Field java…