|
Paper on wildcard instantiations
Generics - Wildcard Instantiation
Lets say, you have a generic type
ValueObjectContainer<T>
and you instantiate this using a lower bounded wildcard
1. ValueObjectContainer<? super IVO> simpleContainer = new ValueObjectContainer<IVO>();
OR
2. ValueObjectContainer<? super IValueObject> simpleContainer = new ValueObjectContainer<IVO>();
OR
3. ValueObjectContainer<? super IValueObject> simpleContainer = new ValueObjectContainer<IValueObject>();
where SimeplVO implements IValueObject which extends from IVO
Now you can execute all generic methods of ValueObjectContainer, given that you are passing a subtype of the bound. Which means in case 1 you can pass
IVO/IValueObject/SimpleVO
in case 2 you can only pass
IValueObject/SimpleVO
if the bound was SimpleVO (as in <? super SimpleVO>):
you would only be able to pass SimpleVO or its subclasses.
Nutshell: you can instantiate using a lower bounded wildcard given the following:
1. The actual parameter type should be a supertype of the bound (or the bound itself)
2. The simple generic methods of the instantiated class can only accept subtypes of the bound (or the bound itself).
What the lower bound "super" does is that it restricts an unrestricted generic type. Which is to say we can instantiate this generic type only with super types of the lower bound
ValueObjectContainer<? super IVO> simpleContainer = new ValueObjectContainer<IValueObject>();
//THIS WILL THROW AN ERROR
Method Calls (Real Deal)
Lets say there's a method in ValueObjectContainer<T>
add(T vo)
In Lower Bound case
Since lower bounds is effectively limiting the classes that can be used to call methods etc. of a given generic class,
it ALLOWS one to pass the bound or its subclasses to the mix
This tells the compiler for certain (at declaration time) that this class will be instantiated using a type parameter
that is either the bound or its super types .. which makes the runtime "predictable" for this declaration
This is similar to the inheritance in old java:
Let's try this by using analogous examples.
In Pre-Tiger days if you have a method
add(IValueObject vo)
You can pass this method instances of IValueObject and all its decedents including SimpleVO AND all of SimpleVO's decedents
BUT you can't pass it IVO or any of its supertypes.
Similarly in Tiger if you have the following declaration for a generic class
ValueObjectContainer<? super IValueObject> simpleContainer
This declaration tells the compiler that we will be instantiating this reference with IValueObject or its supertypes.
Now the compiler is comfortable in this knowledge, and will therefore allow you to call methods of the created instance as long as you are passing subtypes of the bound (IValueObject)
Getting back to the method add(T vo) of ValueObjectContainer. We will be able to call this method as long as we are supplying subclasses of the bound type parameter used in instantiating this class.
Once Again: The bound parameter is the type name that appears after the keyword super in the declaration
In Upper Bound Case
Lets change the declaration to
<? extends IVO>
Now you can instantiate this class using any of IVO's subtypes.This cripples the compiler's ability to know which type you will be using to instantiate this reference.This, consequently, makes the runtime absolutely unpredictable. And therefore, the complier doesn't let you call any generic methods on this class.Note that all regular methods could still be called as in pre-Tiger days.
Now lets talk about a bound defined class
Lets change the class definition
public class ValueObjectContainer<T extends IVO>
Now when you declare a variable of this class in your application, the compiler will preserve the sanctity with the class definition. As an example, lets say we declare a variable of this class like this
ValueObjectContainer<? super IValueObject> simpleContainer; //This will work
But if try to do something like this
ValueObjectContainer<? super Integer> simpleContainer; //This will NOT work
|