A Bridge Too Far
In my last entry I presented a technique for creating strategy classes that take full advantage of any generic metadata that is present in your application. At the end of that entry I showed this code snippet:
EntitlementCalculator calculator = new DividendEntitlementCalculator();
calculator.calculateEntitlement(new MergerCorporateActionEvent());
You'll remember that DividendEntitlementCalculator was defined as:
public class DividendEntitlementCalculator implements EntitlementCalculator<DividendCorporateActionEvent> {
public void calculateEntitlement(DividendCorporateActionEvent event) {
}
}
As such it is not correct to pass an instance of MergerCorporateActionEvent into the calculateEntitlement method of the DividendEntitlementCalculator class. However, as I mentioned in my last entry that code will compile. Why? Well, EntitlementCalculator.calculateEntitlement() is defined to accept any type that extends CorporateActionEvent so it should compile. So in this scenario what happens at runtime and how does Java enforce type safety? Well, as you might imagine running this code gives you a ClassCastException saying that you cannot cast a MergerCorporateActionEvent to DividendCoporateActionEvent. In this way, Java can enforce type safety for you application - there is no way that the MergerCorporateActionEvent can creep into a method where DividendCorporateActionEvent is expected.
The real question here is: 'Where does that ClassCastException come from?' The answer is pretty simple - the Java compiler adds the code to create and throw it as appropriate by introducing a bridge method. Bridge methods are synthetic methods that the compiler will generate and add to your classes to ensure type safety in the face of generic types.
In the case shown above EntitlementCalculator.calculateEntitlement can be called with any object that is type compatible with CorporateActionEvent. However, DividendEntitlementCalculator accepts only objects that are type compatible with DividendCorporateActionEvent, but, since you can call the DividendEntitlementCalculator via the EntitlementCalculator interface it too must accept CorporateActionEvent. So what does this translate to in the compiled class file? We have the user supplied method:
public void calculateEntitlement(DividendCorporateActionEvent event) {
System.out.println(event);
}
Which translates to this bytecode:
public void calculateEntitlement(bigbank.DividendCorporateActionEvent);
Code:
Stack=2, Locals=2, Args_size=2
0: getstatic #2; //Field java…