Reputation: 198
I am using wildcard and lower bound generics on list and yet compiler is throwing error.
Code:
int intStart = 0;
Number num = new Integer(2);
List<? super Integer> listOfNumbers = new ArrayList<>();
listOfNumbers.add(intStart);
listOfNumbers.add(num); //throws compiler error
Error :
The method add(capture#8-of ? super Integer) in the type List is not applicable for the arguments (Number)
With List<? super Integer>
, I should have been allowed to add any objects of type Integer
or its superTypes e.g. Number or Object.
I've gone through some SO discussions, but couldn't find why I should be getting above error.
Upvotes: 3
Views: 993
Reputation: 50716
With
List<? super Integer>
, I should have been allowed to add any objects of type Integer or its superTypes e.g. Number or Object.
That's incorrect. List<? super Integer>
doesn't mean a list that can hold any supertype of Integer. It means a list whose generic type may be some specific supertype of Integer. So it might actually be List<Object>
, List<Number>
or List<Integer>
. All of those list types may contain an Integer
, but the same can't be said for Number
. A Number
might be a Double
or a Long
, which isn't allowed in a List<Integer>
.
To make it a little more concrete, consider this snippet:
Long longValue = 2L;
List<Integer> listOfIntegers = new ArrayList<>();
// listOfIntegers.add(longValue);
The commented line obviously shouldn't compile, and doesn't. But what if we added this:
Number longAsNumber = longValue;
List<? super Integer> listOfSuper = listOfIntegers;
listOfSuper.add(longAsNumber);
This is equivalent to the snippet in your question. Should the final line compile? If it would, it would violate the generic invariant of List<Integer>
.
In response to your comment, List<? super Integer>
is indeed unnecessary in your example. A more common case would be to add flexibility to a method parameter. For example, a method
void addInt(List<Integer> list) {
list.add(1);
}
would only be able to accept List<Integer>
, which would be unnecessarily restrictive. Changing the parameter type to List<? super Integer>
would allow the method to accept List<Integer>
as well as List<Number>
, either of which can contain an Integer
value.
This only works if the list is consuming the generic type in question. If the method tried to produce values from the list, it would be forced to assume they're of type Object
, since we don't have a definite supertype. For more information, see What is PECS (Producer Extends Consumer Super)?
Upvotes: 7
Reputation: 8833
List<? super Integer>
is a bit weird. You can only add Integers (int gets auto-boxed to Integer). You can't add a Number because that is not allowed in a List<Integer>
, even though List<Number>
is a valid assignment to List<? super Integer>
, because you may be working with a List<Integer>
. Since the compiler doesn't know for sure, it won't allow the unsafe operation.
So in short, When using a List<? super Integer>
, The only type safe to read is Object, and the only type safe to write is Integer. The compiler doesn't allow it because it is an unsafe operation.
(For Further reading, this answer gives a pretty good detailed explanation of the generics of this)
Upvotes: 0