Sunday, November 20, 2011

Java Generics Capture Conversion

Introduction: Earlier in my article Java Compile Time Method Binding I had promised to explain how type arguments are inferred during invocation of a generic method. However, that would not be of that use without learning how capture conversion works first. So what is capture conversion. Capture conversion is the type conversion when a reference of a generic type is created by passing the type parameters. Its not really that complicated. But it still does require some attention. Note that capture conversion is only required for compile time type checking, nothing is there in the compiled code and nothing happens at runtime.

An example: Let us assume, we have the following example program.



In the above example, an instance of CaptureExample is created passing the arguments Object and String. After capture conversion, a new type is created where E is replaced by Object and F is replaced by String. So the method copy is now copy(List<Object>,List<String>. Pretty simple. Let's check out the other three cases where wild cards are used.

Unbounded wildcard: If we use unbounded wildcard (?) while creating the reference of a generic type, the capture conversion will replace the type parameter with a type which has an upper bound same as the type parameter itself and lower bound null type.

For example, let's consider the following example.



In this case, the type E is converted to ? extends InputStream, and hence it now the method cannot be invoked with an object of type Object. However, in this case the method readFromIt() simply cannot be invoked on the reference ce. I will discuss this in the following note.

Note: If after capture conversion, an argument of a method does not have a lower bound, the method simply cannot be invoked. This is because, since the argument type does not have a lower bound, there exists no type that the compiler can guarantee to be a subtype (as required by method invocation conversion) of the capture converted type.


Upper bounded wildcard: If the wildcard used during creation of a reference is upper bounded (like E extends SomeType), the resultant type will have both the upper bound as passed and as the original declaration.



How to declare a type parameter extends multiple type: If the type parameter in a generic type declaration need to extend more than one type, the types can be separated using the & operator. For example,
public class CaptureExampleUpperBoundedWildcard<E extends InputStream & Comparable> {
   ...
}


Lower bounded wildcard: Lower bounded wildcards are of the form (? super SomeType). If this is used in the reference creation, the capture converted type will have the upper bound as originally declared and the lower bound SomeType. Let's consider the following example.



In this case the lower bound is FilterInputStream and the upper bound is InputStream.

1 comment: