To detect and memorize whether
super should be used it is worth remembering the Get and Put principle:
Use Upper Bounded Wildcards (i.e., <? extends Number>) when you only get values out of a structure (when you use only getters or similar methods), use Lower Bounded Wildcards (i.e., <? super Integer>) when you only put values into a structure (when you use only setters or similar methods) and do use Unbounded Wildcards (simple <?>) when you both get and put (when it is essential for you to use all kind of methods).
To memorize this principle, you can also use PECS: Producer Extends, Consumer Super. This means that if you get a value from a generic class, method or any other object (it can produce for you what you need), you use
extends. And vice versa, if you put or set a value into a generic class, method or any other object (it can consume what you put in it), you use
Remember, that it is not possible to put anything into a type declared with an
extends wildcard except for the
null value since it can represent any reference type. Similarly, it is not possible to get anything from a type declared with
super wildcard except for a value of an
Object type: a super type for every reference type.
You cannot use a lower and an upper bound simultaneously in wildcards in particular and in type bounds in Java in general.
Note, that a class or an interface that is used after an “extends” or a “super” keyword itself is included in the inheritance. For example,
Box<T> is absolutely compatible and covariant with
Box<? extends T> or
Box<? super T>.
In the end, it is important to note that a frequently used unbounded wildcard
? is equivalent to:
? extends Object.It is interesting that an inheritance prohibition in generics is made specifically to prevent run-time errors: otherwise, generics would lose their type safety feature.
Suppose you have a method that takes as its parameter a collection of things, but you want it to be more flexible than just accepting a
Case 1: You want to go through the collection and do things with each item.
Then the list is a producer, so you should use a
Collection<? extends Thing>.
The reasoning is that a
Collection<? extends Thing> could hold any subtype of
Thing, and thus each element will behave as a
Thing when you perform your operation. (You actually cannot add anything (except null) to a
Collection<? extends Thing>, because you cannot know at runtime which specific subtype of
Thing the collection holds.)
Case 2: You want to add things to the collection.
Then the list is a consumer, so you should use a
Collection<? super Thing>.
The reasoning here is that unlike
Collection<? extends Thing>,
Collection<? super Thing> can always hold a
Thing no matter what the actual parameterized type is. Here you don’t care what is already in the list as long as it will allow a
Thing to be added; this is what
? super Thing guarantees.