Richard and Raoul, who provide in person training courses on Java 8, offered a joint presentation at Devoxx UK 2015 where they discussed the origins and motivations for Generics in Java, some of the less known current features, and a glimpse of what might be coming up in Java 10. The presentation was split into three distinctive sections: past, present and future of Generics.
The Past
As Richard and Raoul mentioned, before the addition of Generics, collections in Java had to work under the assumption that they contained Objects, without being able to be any more specific. If one added an object of any type into a collection, the type information would be lost, and upon retrieval the developer will have to manually cast it to the relevant class to be able to recover it.
This not only created cumbersome code, but also made it impossible to restrict the type of object that was being inserted into a collection. The release of Java 5 in 2004 fixed this by adding Generics into Java, although in order to keep backwards compatibility the implementation of generics had to be done through type-erasure.
The Present
Moving forward, Richard and Raoul talked about some of the current features of generics in Java, particularly the lesser known ones: intersection types, wildcards and F-bound polymorphism.
“Intersection types” is a relatively easy to use feature, and it refers to the ability to indicate that the type within a collection needs to extend two or more other classes or interfaces. Wildcards, on the other hand, are a harder topic. As Richard put it:
Wildcards are a very poorly understood and overly hard to use feature. You can't, in Java, express co-variance and contra-variance of parameters once and for all on the declaration of an interface you need to express it every time a the type is used in a variable definition. This pushes complexity from library authors to everyone.
Despite their difficulties, Richard and Raoul demonstrated how a programmer can benefit from these by associating the different wildcards to the concepts of providers and consumers.
Providers hand out a number of objects for other classes to use. In order for those other classes to know what can be done with those objects, we use a “contract” that indicates the most specific type that is guaranteed to be provided. This “contract” takes the form of the extends wildcard, or more specifically Class<? extends T>. This indicates that the type used by the generic class is T or some subclass of T, which means that you can expect to find in it all the behaviour that is guaranteed by T.
Analogously, consumers accept objects to do something with them. In order for other classes to know what kind of objects can be passed to the consumers, the consumer needs to specify what is the most specific type that is guaranteed to be accepted. This takes the form of the super wildcard, or more specifically Class<? super T>. This notation indicates that we are intending to give this consumer objects of type T, and therefore the consumer needs to be able to handle objects of type T or higher.
Finally, the speakers moved on to F-bounded polymorphism, referring to it as “Curiously Recurring Generics Pattern” (analagous to the Curiously Recurring Template Pattern found in C++). This is a form of generics in which the type argument recursively makes a reference to the generic type itself, taking the form:
public interface ClassName<T extends ClassName<T>>
As indicated in the presentation, this feature is particularly useful when a method in the generic type needs to return the template type, like for instance when cloning the object.
The Future
In the last part of the presentation, the speakers went on to introduce some of the improvements on generics currently being discussed and potentially targeted at Java 10. On one side, work is being done to be able to use generics over primitives, removing the need for autoboxing and deboxing; this is closely related to project Valhalla. As Richard explained, these modifications would allow the programmer to write code like this:
List<int> integers = new ArrayList<>(); integers.add(1);
This, Richard said, would be implemented under the hood as int[], rather than the current Integer[], saving both memory and allowing the CPU's cache pre-fetcher to do a better job. “This will result in performance improvements along the same lines as c#'s reified generics”, he added.
On the other side, an effort may be made towards simplifying the handling of co-variance and contra-variance through allowing library authors to indicate variance at interface declaration, as opposed to at interface usage as is the case now. This form of declaration-site variance would allow most developers to benefit from the wildcard generics without having to know too much about them, while advanced programmers can still override this default behaviour for more specialised code.