Sometimes the fact that generics are invariant makes things difficult. Imagine the code:
class ArrayList<E> implements List<E> {
...
boolean addAll(Collection<E> c) {...}
...
}
List<String> strings = new ArrayList<>();
List<Object> objects = new ArrayList<>();
objects.addAll(strings); // compile-time error!
To improve the situation wildcards could be used.
Wildcard has the form:
? extends ReferenceType ? super ReferenceType
If S is a subtype of T then
type List<S> is considered to be a subtype of List<? extends T>.
If S is a supertype of T then List<S> is considered to be a subtype of List<? super T>.
With wildcards code from the first step has the form:
class ArrayList<E> implements List<E> {
...
// Anything more special than E could be added into the list as well as E
boolean addAll(Collection<? extends E> c) { ... }
...
}
List<String> strings = new ArrayList<>();
List<Object> objects = new ArrayList<>();
objects.addAll(strings); // completly fine (all Strings are objects)!
strings.addAll(objects); // compile time error (not all Objects are Strings) !
To detect whether extends or super should be used it's worth to remember the Get and Put principle:
In the previous example we took elements out of c collection, so we use extends keyword.The Get and Put Principle: use an extends wildcard when you only get values out of a structure, use a super wildcard when you only put values into a structure, and don’t use a wildcard when you both get and put.
Let's look at the examples from java.util.Collections:
Method unmodifiableList creates new List object reading objects from the list provided:According to Put and Get principle extends is used since we consuming elements from original list. This makes possible to create unmodifiable list of numbers using list of integers:public static <T> List<T> unmodifiableList(List<? extends T> list)
List<Number> numbers = Collections.<Number>unmodifiableList(new ArrayList<Integer>());
When used in fill method, the list is used only to put elements in it, that's why super is used.
public static <T> void fill(List<? super T> list, T obj) { ... }
Using super makes perfectly fine to use fill as:
// Since Interger is a subtype of Number we can guarantee that newly created array will have only Numbers in it
Collections.<Integer>fill(new ArrayList<Number>, new Integer(10));
The Get and Put Principle works in the opposite direction as well: if the structure is declared with extends you can only get values from it. If it's declared with super – you can only put values into it. The compiler knows about this:
List<? extends Number> list = new ArrayList<>();
list.add(0); // Get and Put principle violation, compile-time error!
And that's why:
class Example {
public static void addToList(List<? extends Number> numbers) {
// If we will allow addition to the list both should be allowed
numbers.add(5);
numbers.add(1.5);
}
}
public class Main {
public static void main(String[] args) {
List<Integer> integers = new ArrayList<>();
// We introduced wildcards to be able to do things like this
Example.addToList(integers);
// The integer is... a double. But that's nonsense!
Integer nonsense = integers.get(1);
}
}
As we see, ability to add to structure declared with extends leads to nonsense.
The frequently used unbounded wildcard:?
is equivalent to:? extends Object