I know that there was a similar question already posted, although I think mine is somewhat different...
Suppose you have two methods:
// Bounded type parameter
private static <T extends Number> void processList(List<T> someList) {
}
// Upper bound wildcard
private static void processList2(List<? extends Number> someList) {
// ...
}
As far as I know, both methods accepts arguments, that are List
of type Number
or List
of subtype of Number
.
But what's the difference between the two methods after all?
There are several differences between the two syntaxes during compile time :
- With the first syntax, you can add elements to
someList
but with the second, you can't. This is commonly known as PECS and less commonly known as the PUT and GET prinicple.
- With the first syntax, you have a handle to the type parameter
T
so you can use it to do things such as define local variables within the method of type T
, cast a reference to the type T
, call methods that are available in the class represented by T
, etc. But with the second syntax, you don't have a handle to the type so you can't do any of this.
The first method can actually be called from the second method to
capture the wildcard. This is the most common way to capture a
wildcard via a helper method.
private static <T extends Number> void processList(List<T> someList) {
T n = someList.get(0);
someList.add(1,n); //addition allowed.
}
private static void processList2(List<? extends Number> someList) {
Number n = someList.get(0);
//someList.add(1,n);//Compilation error. Addition not allowed.
processList(someList);//Helper method for capturing the wildcard
}
Note that since generics are compile time sugar, these differences at a broader level are only limited to the compilation.
The difference is on the compiler side.
On the first one you can use the type (to cast something or use it as a bound to call another method for example) while on the second one, you cannot use it.
If you want to use the type information then go with bounded. With the wildcard, the argument will appear as a generic Object and you won't be able to call methods based on that type.
public static <T extends Object> ListIterator<T> createListIterator(ListIterator<T> o)
{
return new ListIteratorAdaptor<T>(o);
}
- https://docs.oracle.com/javase/tutorial/java/generics/bounded.html
I can think of the below differences :
a) Modifying your list inside the method, consider below code :
private static <T extends Number>void processList(List<T> someList)
{
T t = someList.get(0);
if ( t.getClass() == Integer.class )
{
Integer myNum = new Integer(4);
someList.add((T) myNum);
}
}
// Upper bound wildcard
private static void processList2(List<? extends Number> someList)
{
Object o = someList.get(0);
if ( o instanceof Integer )
{
Integer myNum = new Integer(4);
someList.add(myNum); // Compile time error !!
}
}
With wildcard you cannot add elements to the list ! Compiler tells you that it doesn't know what is myNum
. But in the first method, you could add a Integer
by first checking if T
is Integer
, no compile time error.
b) The first method is called generic method. It follows the syntax that is defined for a generic method.
The upper bounds specified in the method definition are used to restrict the parameter types.
The second one is NOT necessarily called a generic method, it is a normal method that happens to accept a generic parameter.
The wildcard ?
with extends
keyword is used as a means of relaxing the types that the method can accept.
There are following three types of Wildcard usually used with Generic in JAVA. Each one is explained as below with example.
Upper-bounded Wildcard:
? extends T : In Upper bounded wildcard only T or its subtypes will be supported.
For example we have an Animal class and have Dog , Cat as its subtypes. So following generic methods will only
accept parameters of type Data<Animal>, Data<Dog> and Data<Cat>
public static void add(Data<? extends Animal> animalData) {
}
Lower-bounded Wildcard:
? super T : In Lower-bounded wildcard only T or its super types will be supported.
Same example we used for defining Lower-bounded Wildcard. Lets say we have Animal class as super or parent class
and Dog as its child class. Now below method use Lower-bounded Wildcard and will only accept parameters of type
Data<Animal>, Data<Dog> and Data<Object>
public static void add(Data<? super Dog> animalData) {
}
Unbounded Wildcard:
? : Unbounded wildcard supports all types. So our above example method can take parameters of type
Data<Animal>, Data<Dog> , Data<Object> and Data<Cat>
public static void add(Data<?> animalData) {
}