Bugs<? extends Generics & Bugs<? extends Generics & Bugs < ... >>>
By Maurizio Cimadamore on Jun 04, 2008
As Alex pointed out in his JavaOne talk Upcoming Java programming language features in JDK7, not all the language features have the same degree of complexity. A good example of how adding a new feature can become problematic is provided by JSR14. The big issue with generic types is that they affect deeply and orthogonally the JLS, not only because the JLS has now to deal with parameterized classes and methods, but also because the JLS should consider also those types that can be regarded as a sub-product of the generification process: wildcard types, raw types, intersection types, type variables, captured type variables, infinite types (see the title!?) and so on (not all of those types are directly available to the programmer for use - they are internally exploited by the compiler in order to perform some specific tasks, e.g. type-inference). This is a big list, especially if you think that the JLS should also take into consideration all the possible interactions between those types and the ones that were already available in the Java platform (refer to the subtyping section of the JLS to get an idea of what this means).
The chart below shows the correlation between generics and other Java features (the numbers come from the data I've been collecting since I started working on generic bugs). Not surprisingly, it seems that the most recurrent feature developers are reporting bugs against is casting conversion.
|What makes generic cast so difficult to implement in javac? Casting conversion is one of those features that heavily relies upon subtyping (roughly speaking, given two types S and T, a cast conversion between S and T exist if either S is a subtype of T or vice-versa). As mentioned earlier, subtyping rules have changed a lot in the transition from Java 1.4 to Java 5, and those changes are mainly due to the support for generic types in the platform. As an example, consider capture conversion; its addition to the language allows for a smooth interoperability between generic methods and wildcards (e.g. you can call a generic method accepting a List<Z> with an actual argument whose type is List<?>). However, this flexibility comes at a price: capture-conversion replaces every wildcards with a so-called captured type-variable (so that List<?> becomes List<#CAP> where #CAP is some captured type-variable). Captured type-variables have the important property of being basically incompatible with themselves (see why).|
For example, given two types, List<#CAP1> and List<#CAP2>, it's generally not safe to assume that one is a subtype of the other (and viceversa), regardless of whether the captured-variables' bounds are the same or not. This simple rule makes it very difficult to handle casting conversion properly since, on the one hand, the JLS mandates (via subtyping rules and via type-containment rules) that a captured type cannot subtype another captured type (unless they are the same type - which most of the time is quite useless). If no such subtyping is given, then it's obviously difficult to interpret casting conversion rules. What the compiler currently does is to implement a relaxed version of the subtyping rules that has been explicitly designed for making casting conversion implementable. This version of the subtyping algorithm takes into account e.g. the bounds of a captured type-variable; this way javac can do smarter assertions as to whether a capture-converted S type might or might not be castable to another type T.
This should suggest the idea that it is crucial, from a language-design perspective, that any interaction between a proposed new feature with features that already are in the language is clarified before the extension to the language is made. As far as JSR14 is concerned, the task of detecting such potentially dangerous interactions was complicated by the fact that, back in 2004, the only 'real' Java application exploiting generic types was javac itself! This made it almost impossible to detect in advance all the issues that can arise with certain usages of generic types. Hopefully the situation it's a bit different now; there is a much larger generic code base out there (yes, javac is not alone anymore!) which means that now we might have the means for addressing the majority of those bugs. Also don't forget that, thanks to the OpenJDK initiative, people are now free to
get involved, experiment and explore new language features as they are being developed; this, again, means that such new features can be tested more effectively before they get included into the
main JDK code base.
Thanks to Jonathan for his useful comments on this post.