This was adapted from The Java tutorial and Question 445 from Piazza.
upper-bounded wildcard
<? extends T>
is known as the upper-bounded wildcard. This is used to relax the use of complex type to its co-variant complex types, i.e.complex types with type parameter being subtype of T
. So, List<? extends T>
means a List of subtypes of T.
Short example:
consider the following process
method:
public static void process(List<? extends T> myList) {
for (T ele : myList) {
// ...
}
}
myList
is the a list of T or subtype of T, thus no matter what runtime-type ele
is, we can always assign compile time type T to it.
Notice, however, that it is better not to initialize a list with an upper-bounded wild card.
ArrayList<? extends Number> list = new ArrayList<>();
list.add(1);
The code above will actually give rise to an error of incompatible types. One possible explanation is that the type parameter of list is not determined, which can be of type Integer, Double, or any other subtypes of Number. Since it is possible for the type to be of type Double, storing an integer into the list will be invalid as an integer is not a double value. The same applies for the reason why a double value cannot be added to the list.
lower-bounded wildcard
Similarly, <? super T>
is known as the lower-bounded wildcard, which relaxes the use of complex type to its contra-variant complex types, i.e.complex types with type parameter being supertype of T
. So, List<? super T>
means a List of supertypes of T.
Short example:
consider the following adding
method:
public static void adding(List<? super T> myList) {
myList.add(new T());
}
myList
is the a list of T or supertype of T, thus it is always compatible to add object of type T into it since T is the subtype of supertype of T
One of the more confusing aspects when learning to program with generics is determining when to use an upper bounded wildcard and when to use a lower bounded wildcard. This page provides some guidelines to follow when designing your code.
For purposes of this discussion, it is helpful to think of variables as providing one of two functions:
An "In" Variable An "in" variable serves up data to the code. Imagine a copy method with two arguments: copy(src, dest). The src argument provides the data to be copied, so it is the "in" parameter. An "Out" Variable An "out" variable holds data for use elsewhere. In the copy example, copy(src, dest), the dest argument accepts data, so it is the "out" parameter.
Of course, some variables are used both for "in" and "out" purposes — this scenario is also addressed in the guidelines.
You can use the "in" and "out" principle when deciding whether to use a wildcard and what type of wildcard is appropriate. The following list provides the guidelines to follow:
Wildcard Guidelines:
More quetions and exercises on Java tutorial