Java generics super keyword
I didn't get it for a while. Many of the answers here, and the other questions show specifically when and where certain usages are errors, but not so much why.
This is how I finally got it. If I have a function that adds Number
s to a List
, I might want to add them of type MySuperEfficientNumber
which is my own custom class that implements Number
(but is not a subclass of Integer
). Now the caller might not know anything about MySuperEfficientNumber
, but as long as they know to treat the elements added to the list as nothing more specific than Number
, they'll be fine.
If I declared my method as:
public static void addNumbersToList(List<? extends Number> numbers)
Then the caller could pass in a List<Integer>
. If my method added a MySuperEfficientNumber
to the end of numbers
, then the caller would no longer have a List
of Integer
s and the following code wouldn't work:
List<Integer> numbers = new ArrayList<Integer>();
addNumbersToList(numbers);
// The following would return a MySuperEfficientNumber not an Integer
Integer i = numbers.get(numbers.size()-1)
Obviously this can't work. And the error would be inside the addNumbersToList
method. You'd get something like:
The method add... is not applicable for the arguments (MySuperEfficientNumber)
Because numbers
could be any specific kind of Number
, not necessarily something that MySuperEfficientNumber
is compatible with. If I flipped the declaration around to use super
, the method would compile without error, but the caller's code would fail with:
The method addNumbersToList(List<? super Number>)... is not applicable for the arguments (List<Integer>)
Because my method is saying, "Don't think that your List
can be of anything more specific than Number
. I might add all sorts of weird Number
s to the list, you'll just have to deal with it. If you want to think of them as something even more general than Number
-- like Object
-- that's fine, I guarantee they'll be at least Number
s, but you can treat them more generally if you want."
Whereas extends
is saying, "I don't really care what kind of List
you give me, as long as each element is at least a Number
. It can be any kind of Number
, even your own weird, custom, made-up Number
s. As long as they implement that interface, we're good. I'm not going to be adding anything to your list since I don't know what actual concrete type you're using there."
For the first part List<Number>
fits in List<? super Number>
but you can't add an Object
to a List<Number>
. That's why you can't add an Object
to List<? super Number>
.
On the other hand you can add every subclass of Number
(Number
included) to your list.
For the second part, String
is an Object
, but String
isn't a superclass of Number
.
If it worked like this, as every class is a subclass of Object
, super
would have no meaning.
Let's see every possible cases with List<? super Number>
:
- The passed list is a
List<Object>
List<Object>
will workObject
fits in<? super Number>
- You can add any subtype of
Number
to aList<Object>
- Even if you could also add
String
in it the only thing you're sure of is that you can add any subclass ofNumber
.
- The passed list is a
List<Number>
:List<Number>
will workNumber
fits in<? super Number>
- You can add any subtype of
Number
to aList<Number>
- The passed list is a
List<Integer>
(or any subclass ofNumber
):List<Integer>
won't work- Integer is a subclass of
Number
so it is exactly what we want to avoid - Even if an
Integer
fits in aNumber
you wouldn't be abble to add any subclass ofNumber
in aList<Integer>
(for example aFloat
) super
doesn't mean a subclass.
- The passed list is a
List<String>
(or any class not extendingNumber
nor in the "super hierarchy" ofNumber
(ie.Number
andObject
) :List<String>
won't workString
doesn't fit inNumber
"super hierarchy"- Even if
String
fits inObject
(which is a super class ofNumber
) you woudln't be sure to be able to add aNumber
to aList
that contain any subclass from one of the super classes ofNumber
) super
doesn't mean any subclass of one of the super classes, it only means one of the super classes.
How does it work ?
You could say that as long as you can add any subclass of Number
with your typed List
, it respects the super
keyword.
The bounded wildcard in List<? super Number>
can capture Number
and any of its supertypes. Since Number extends Object implements Serializable
, this means that the only types that are currently capture-convertible by List<? super Number>
are:
List<Number>
List<Object>
List<Serializable>
Note that you can add(Integer.valueOf(0))
to any of the above types. however, you CAN'T add(new Object())
to a List<Number>
or a List<Serializable>
, since that violates the generic type safety rule.
Hence it is NOT true that you can add
any supertype of Number
to a List<? super Number>
; that's simply not how bounded wildcard and capture conversion work. You don't declare a List<? super Number>
because you may want to add an Object
to it (you can't!); you do because you want to add Number
objects to it (i.e. it's a "consumer" of Number
), and simply a List<Number>
is too restrictive.
References
- Angelika Langer's Generics FAQs
- What is a bounded wildcard?
- When would I use a wildcard parameterized type with a lower bound? ("When a concrete parameterized type would be too restrictive.")
- Why is there no lower bound for type parameters? ("Because it does not make sense.")
- JLS 5.1.10 Capture Conversion
See also
- Effective Java 2nd Edition, Item 28: Use bounded wildcards to increase API flexibility
- "PECS stands for producer-
extends
, consumer-super
- "PECS stands for producer-
Related questions
- Too many to list, PECS,
new Integer(0)
vsvalueOf
, etc