According to the book "Effective Java" of Joshua Bloch there is a rule about how/when use the bounded wildcards in generics. This rule is PECS (Producer-Extends, Comsumer-Super). When I study the following example:
Stack<Number> numberStack = new Stack<Number>();
Iterable<Integer> integers = ... ;
numberStack.pushAll(integers);
I understand that this rule fits perfect in this example. I have to declare the method pushAll as the following sample:
// Wildcard type for parameter that serves as an E producer
public void pushAll(Iterable<? extends E> src) {
for (E e : src)
{
push(e);
}
}
But what happens if I have the following example?
Stack<Integer> integerStack = new Stack<Integer>();
Iterable<Number> numbers = ... ;
integerStack.pushAll(numbers);
I have to declare the pushAll as it follows:
public void pushAll(Iterable<? super E> src) {
for (E e : src)
{
push(e);
}
}
According to PECS rule the above declaration is wrong. But I want to have a Stack of Integers and pass to this Stack a Number. Why not to do it?
Why should I always use the extends keyword? Why using super is wrong?
Of course the same stands for the comsumer's point of view. Why a consumer should always be super?
PS: To be more specific you can find this above example at the sector "Item 28" of the referred book.
When you declare a Stack<Foo> you mean a Stack of Foos, or subclasses of Foo. As an example, you would expect to be able to put a String in a Stack<Object>. The other way is not true, you should not be able to insert another Object, in a Stack<String>.
In your example you declare a Stack<Integer>. You should be able to put Integers in this stack, but not other Numbers (like a Double), which you would if you declared the parameter <? super E>. That's why the put-method should have a paramter of the type <? extends E>.
Trying to store arbitrary numbers in a Stack can't possibly work, since a Number could be something other that an Integer. So your example doesn't make much sense.
You would use super when the object asts as a consumer, i.e. when instances of the generic type of the object are passed as arguments to methods of the object. For example:
Collections.sort(List<T>, Comparator<? super T>)
In this example, the sort method takes T instances from the collection, and passes them as argument to the compare(T o1, T o2) of the comparator.
Contrast this to your first example, where the Iterable src is a producer. The pushAll() method calls a method of the Iterable which roduces (i.e. returns) instances of T. In this case, the iterable is a producer, hence the use of ? extends T
In the pushAll method, you are not passing type E, but any type that extends E. So, instead of passing an Iterable of Numbers, you can pass any Iterable of a type that extends Number.
The original example uses a Number type because you can then pass any type that is a subclass of Number, like Integer, BigDecimal and so on.
In your example, you are doing it the other way around. You are using Integer to declare your Stack. Therefore, pushAll will only be able to accept those classes that are extended by Integer. You will not be able to use Numbers (or any other class, because Integer is a final class).
First thing to notice is that Integer extends Number, so you shouldn't be pushing Number objects into a Stack of Integers. However, the first sample will work with Integers, Floats, BigDecimal and all other Number subclasses.
Your example doesn't make much sense. A construct like <? extends Number> means that Number and every type is allowed which inheits from Number. So you define an upper and a lower boundary, from type Number down to the most specific one. The other way round, <? super Number> means that Number and any of its supertyes are allowed. Since Number extends Object and implements Serializable the following three types are allowed:
java.lang.Number
java.lang.Object
java.io.Serializable
In your example you declare the generic type Stack<Integer>. Let's consider the following.
Your Stack is never be able to hold items of any super type of Integer
Your Stack is never be able to hold items of any subtype of Integer, since Integer class is final and thus it can't be subclassed.
So, if you want to declare the generic type Stack<Integer>, your iterable is of type Iterable<Integer> and thus your Stack can only hold items of type Integer. You are totally right with the mnemonic PECS, but this only works if you have choosen a concrete type which has at least one super type and at least one subtype.
Related
Most questions about wildcards want to know why something sensible is rejected by the compiler. My question is the opposite. Why is the following program accepted by the compiler?
void test(List<? extends Number> g1, List<? extends Number> g2)
{
g1 = g2;
}
I tried to explain this from the Java Language Specification, but I have not found the answer. I had the impression from various descriptions of Java generics and wildcards that each use of a wildcard is captured as a completely new type, but apparently not here. I have not found any nasty behavior that follows from this assignment being allowed, but it still seems "wrong".
When I face these questions, I approach this in a slightly different manner.
First of all, every single wildcard is captured, everywhere, by javac. In plain english: every time javac "sees" a wildcard it is going to transform that (this is almost accurate as you will see further). Specifically, let's say we have this:
List<? extends Number> list;
javac will transform to:
List<X1> list
where X1 <: Number, where <: means it is a subtype of, as such : X1 is an unknown type that extends Number. This will happen for every single occurrence. And it might be very weird, at first, in some scenarios:
public static void main(String[] args) {
List<?> l = new ArrayList<String>();
one(l);
two(l, l); // fails
}
public static <T> void one(List<T> single){
}
public static <T> void two(List<T> left, List<T> right){
}
capture conversion was applied individually to each List, it's like this happened:
two(List<X1>, List<X2>)
Now to why is your example accepted, is far more interesting, imho. You know that capture conversion is applied, but according to the JLS it is not applied everywhere:
If the expression name is a variable that appears "on the left hand side", its type is not subject to capture conversion.
It's like saying that only values are capture converted, not variables.
So in this case:
g1 = g2;
g1 has not been capture converted, while g2 has. It's like doing:
List<? extends Number> g1 = List<X1> (g2) // pseudo-code
We know that X1 <: Number so, as such List<X1> is a subtype of List<? extends Number>, so the assignment works.
Even if you change ? extends Number to ? (this is not a bounded wildcard anymore), this would still work.
List<? extends Number> is best read as:
This is a list of numbers, but, covariantly.
In other words, this is a list of some concrete but unknown type. However, I do know that, whatever type it might be, at least it is either Number or some subclass thereof.
Generics is weird; once you opt into some variance, you get the restrictions to go along with that. In the case of collections, 'covariance' comes with the baggage of 'no adding'.
Try it.
g1.add(XXX);
the only thing that is legal for XXX here? null. That's literally it. The full and complete and exhaustive list of all you can add to this thing. certainly Number x = 5; g1.add(x); is not going to be allowed by javac here.
By writing List<? extends a thingie> you're saying: Yeah, I want that. I'm signing up to this restriction that I get to add absolutely nothing (other than the academic case of literal null). In trade for handcuffing yourself, the things you can pass in for g1 is expanded considerably.
You can also opt into contravariance:
void foo(List<? super Integer> list) {
list.add(Integer.valueOf(5)); // works!
Integer x = list.get(0); // no go
}
contravariance is the opposite. add works. get doesn't work. Which in this case means: The type of the expression list.get(0) is just.. Object.
Now that we've covered that:
void test(List<? extends Number> g1, List<? extends Number> g2) {}
means 'my first parameter is a list of numbers, but I opt into covariance handcuffs', and 'my second parameter is a list of numbers, but I also opt into covariance handcuffs for this one too', it now makes sense why java lets you write g1 = g2. g2 is guaranteed to be an X<Y>, where X some concrete subclass of List, and Y is either Number or some subclass thereof.
This is 100% compatible, type-wise, with the notion of 'some sort of list whose type param is some covariant take on Number'. The only thing you can do a List<? extends Number> is to invoke methods of List where any T in the signatures are 'disabled' for parameters, and replaced by the bound (Number) for return types.
That's.. exactly what List<? extends Number> is describing, so it's compatible.
"I had the impression from various descriptions of Java generics and wildcards that each use of a wildcard is captured as a completely new type, "
That statement is correct.
So what? You are confusing the type of the object with the type of the variable.
Consider this code:
String s = "abc";
Object o = s;
o has type Object which is assignment compatible with the type of s. But that doesn't mean String and Object are the same type. No different with your example. You have two different List types for the objects, but one type for the variables. Each variable has type List<? extends Number>, so the assignment is fine. When you make the assignment, the object's generic type is List<x> for some completely new unknown type x. But the variable type remains List<? extends Number>.
How could it not be valid?
Both variables have identical type (in this case List<? extends Number>), so the compiler must allow assignment of one to the other.
The objects assigned to the variables may have different types, but the variable types are identical, so assignment is always legal.
The compiler does not know or care what the actual type of an object assigned to a variable is, even if it can be determined from the code. It cares only about declared types when checking types.
In Java it is legal to create a comparator object like this:
Comparator<? super Number> comparator = new Comparator<Object>() {
#Override
public int compare(Object o1, Object o2) {
// implementation here
}
};
But you can't use this object for a simple call like this:
Object o1 = new Object(), o2 = new Object();
comparator.compare(o1, o2);
Why so? I thought ? super Number means Number and all its superclasses.
Suppose instead you had used ? super Double:
Comparator<? super Double> comparator = new Comparator<Object>() {
// ...
};
What you are telling the compiler is that you have a Comparator which
might be a Comparator<Double>, or
might be a Comparator<Number>, or
might be a Comparator<Object>, or
might be a Comparator<Serializable>.
Even though you initialized it with a Comparator<Object>, the compiler doesn’t know that, because the compiler doesn’t execute code. The compiler only knows that the variable holds a Comparator of one of the above types.
Since the variable might hold a comparator of a type more specific than Object, the compiler will not assume that it’s okay to pass Object.
Comparator<? super Number> comparator means that comparator refers to something known to be able to accept a Number (or it's null).
A Comparator<Object> can accept a Number; but the compiler doesn't know if the value referred to by a Comparator<? super Number> is a Comparator<Object>, or a Comparator<Number> any more. All it knows is that it's something that can accept a Number, so that's all it will allow you to pass.
Why so? I thought ? super Number means Number and all its superclasses.
No: it means that Number is an acceptable type to pass as a parameter. To put that another way, Number is a subtype of the acceptable parameter type. To put that another way, the acceptable parameter type is a supertype of Number, so it's fine to pass a Number.
See: What is PECS (Producer Extends Consumer Super)?
The reason why o1 and o2 cannot be used as parameters is because they are "more general" than Number and hence they may point to an instance of a class that is part of a different hierarchy starting at some point in parent chain of Number. Something like, Son class is directly in the hierarchical line of Father, but not in the hierarchy of Grandfather class's SecondSon. So, a reference to an instance of Grandfather class cannot be passed since it could be pointing to an instance in its SecondSon's hierarchy.
I have tried to explain a similar point here.
I'm trying to design my own programming language, and am thinking about generics. I've been doing Java for quite a while now and know about the extends and super generic bounds.
I'm reading this post and trying to understand the need for the lower bounds.
In my language, I am planning to do generics the same way as a regular field, if you say List<MyObject>, you can store either a MyObject, or any subtype of MyObject. Makes sense right?
On the post, they have the following class hierarchy:
class Person implements Comparable<Person> {
...
}
class Student extends Person {
...
}
They then have a sort method:
public static <T extends Comparable<T>> void sort(List<T> list) {
...
}
What I think, is that you should be able to send a List<Student> to this method. As a Student extends Person, the compare method would be handled by it's superclass, Person.
The reason for the error message is that the compiler infers the type parameter of the sort method as T:=Student and that class Student is not Comparable<Student> . It is Comparable<Person> , but that does not meet the requirements imposed by the bound of the type parameter of method sort. It is required that T (i.e. Student ) is Comparable<T> (i.e. Comparable<Student> ), which in fact it is not.
The above doesn't make any sense to me...you should be able to do student.compare(person), so why doesn't this work?
Maybe it's saying that Student should implement it's own comparable method so that Student has a say in the comparison? You don't need to do anything special, just override Person's method. You won't be able to guarantee you are comparing to another Student, but that can be checked with instanceof.
Is there something I'm missing here?
And after all this thinking, I'm now wondering what the purpose of extends is. From my understanding, in a List<MyType>, you can only put a MyType in, not any of it's subclasses. As mentioned above, this doesn't make any sense to me and you should be able to put any subclass in the list like a field.
I should probably make this clear, it's not "why doesn't it work in Java", but "why doesn't it work in generics theory". I just tagged java because that is where I'm making my comparisons.
First: The method declaration
public static <T extends Comparable<T>> void sort(List<T> list)
does not make much sense for me. I thing it should be
public static <T extends Comparable<? super T>> void sort(List<T> list)
Then it would be possible to write sort(listOfStudents). Now I will explain the advantage of upper and lower bounded wildcards:
Polymorphism of type parameters is not transferred to it's generic type
This mean a list of students (List<Student>) is not a list of persons (List<Person>). A instruction like
List<Person> list = new List<Student>();
would fail in Java. There is a simple reason: list.add(new Person()); would be illegal for a list of students but not for a list of persons.
Upper Bounded Wildcards
But maybe you have a function which doesn't care whether the objects are subclasses or not. For example: You could have a method like this:
void printAll(List<Person> list)
They just print some data about all persons to stdout. If you have a list of students (List<Student> listOfStudents) you could write:
List<Person> listOfPersons = new ArrayList<>();
for (final Student student : listOfStudents) {
listOfPersons.add(student);
}
printAll(listOfPersons);
But you may see that it isn't a very nice solution. Another solution would be to use upper bounded wildcards for printAll:
void printAll(List<? extends Person> list)
You can write something like Person person = list.get(0) in printAll. But you cannot write print.add(new Person()) because list could be a list of students or something else.
Lower Bounded Wildcards
Now the same in the other direction: Lets say you have a function which generates some students and put them in a list. Something like this:
void generateStudents(List<Student> list) {
for (int i = 0; i < 10; ++i) {
list.add(new Student());
}
}
Now you have a list of persons (List<Person> listOfPersons) and want to generate students in this list. You could write
List<Student> listOfStudents = new ArrayList<>();
generateStudents(listOfStudents);
for (Student student : listOfStudents) {
listOfPersons.add(student);
}
You may see again, that it is not a very nice solution. You could also change the declaration of generateStudents to
void generateStudents(List<? super Student> list)
Now, you can just write generateStudents(listOfPersons);.
I think your confusion may be coming from the fact that while elements of List<Student> can be compared to each other by virtue of the fact that class Student subclasses Person which implements Comparable<Person> (class Student therefore inherits compareTo(Person o), which can be called with an instance of Student), you still cannot call the sort method with a List<Student>...
The problem is that when the Java compiler encounters the statement:
sort(studentList);
Where studentList is an instance of parameterized type List<Student>, it uses type inference to infer that the type argument to the sort method T is Student, and Student does not satisfy the upper bound: Student extends Comparable<Student>. Therefore, the compiler will throw an error in this case, telling you that the inferred type does not conform to the constraints.
The article that you linked to shows you that the solution to this is to re-write the sort method as:
public static <T extends Comparable <? super T > > void sort(List<T> list)
This method signature loosens the constraint on the type parameter so that you can call the method with a List<Student>.
I'm not too clear on the last part of your post:
in a List<MyType>, you can only put a MyType in, not any of it's subclasses.
If you're referring to the elements of List<MyType>, yes, you can put any element that is a subtype of MyType in this list, say, MySubType. If you're referring to a variable with List<MyType> as its reference type, then no, you cannot put a reference to List<MySubType> in a variable of type List<MyType>. The reason for this is easy to see when you consider the following:
List<MyType> a;
List<MySubType> b = new ArrayList<>();
a = b; // compile-time-error, but assume OK for now
a.add(new MyType()); // Based on the type of a, this should be OK, but it's not because a is actually a reference to List<MySubType>.
I also think you should refer to Java Generics by Wadler and Naftalin, an excellent introduction to Java (5+) Type System.
When you ask "what is the purpose of extends keyword?" based on your observations about collections of objects, the first thing you should remember is generic collections are tricky. I quote from the Wadler/Naftalin book (emphasis mine):
In Java, one type is a subtype of another if they are related by an
extends or implements clause: Integer is a subtype of Number.
Subtyping is transitive.
If A is a subtype of B, B is the supertype of A.
Liskov’s Substitution Principle tells us that wherever a value of one
type is expected, one may provide a value of any subtype of that type:
a variable of a given type may be assigned a value of any subtype of
that type, and a method with a parameter of a given type may be
invoked with an argument of any subtype of that type.
It’s because of violation of Liskov’s Substitution Principle (that
would arise very rapidly in practice) that a List<Integer> is not a
subtype of List<Number> although Integer is a subtype of Number. The
other way round also does not work because List<Number> is NOT a
subtype of List<Integer>.
This paragraph should help us understand why the keyword extends is essential to support inheritance and polymorphism, and yet it (sort of) comes in the way of generic collections.
I think your question would be stated better as: "What are valid use cases for using lower bound generics in JAVA"? I had the same question and it makes sense to use the upper bounds when you have a list and you want to use different types that are all subtypes or the supertype of a hierarchy of classes.
For example, you want methods to populate a list of numbers with int, double, and long. Using extends you can do this as they are all subtypes of Number.
On the other hand, if you want to restrict methods to only use Integer, then you want the more narrowly defined class so that only int is allowed, not float, double, etc.
From the java docs:
The abstract class Number is the superclass of platform classes
representing numeric values that are convertible to the primitive
types byte, double, float, int, long, and short.
The Integer class wraps a value of the primitive type int in an
object. An object of type Integer contains a single field whose type
is int.
A better example might be work you are doing at the JVM level, so you want to restrict your methods to use the VirtualMachineError to get specific information written to the logs, rather than using the Error class, from which it inherits, to limit the number of errors you write to the logs for something very refined, possibly relating to some debugging task.
These are obviously contrived examples, but the theme is the lower bounds can be used to restrict method type parameters.
I went through these topics
Generics..? Super T
Bounding generics with 'super' keyword
However, I still seem to be kind of lost with super keyword:
When we declare a collection like that:
List<? super Number> list = null;
list.add(new Integer(0)); // this compiles
list.add(new Object()); // this doesn't compile
shouldn't it be the opposite - we have a list that contains some objects (of unknown type) which are parents of Number. So Object should fit (since it is the parent of Number), and Integer shouldn't. The opposite is the case for some reason.
Provided we have the following code
static void test(List<? super Number> param) {
param.add(new Integer(2));
}
public static void main(String[] args) {
List<String> sList = new ArrayList<String>();
test(sList); // will never compile, however...
}
It is impossible to compile the above code (and my sanity suggests that this is the right behaviour), but the basic logic could prove the opposite:
String is Object, Object is superclass of Number. So String should work.
I know this is crazy but isn't this the reason why they didn't allow <S super T> constructs? If yes, then why <? super T> is allowed?
Could someone help me restore the missing part of this logic chain?
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
Related questions
Too many to list, PECS, new Integer(0) vs valueOf, etc
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 work
Object fits in <? super Number>
You can add any subtype of Number to a List<Object>
Even if you could also add String in it the only thing you're sure of is that you can add any subclass of Number.
The passed list is a List<Number> :
List<Number> will work
Number fits in <? super Number>
You can add any subtype of Number to a List<Number>
The passed list is a List<Integer> (or any subclass of Number):
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 a Number you wouldn't be abble to add any subclass of Number in a List<Integer> (for example a Float)
super doesn't mean a subclass.
The passed list is a List<String> (or any class not extending Number nor in the "super hierarchy" of Number (ie. Number and Object) :
List<String> won't work
String doesn't fit in Number "super hierarchy"
Even if String fits in Object (which is a super class of Number) you woudln't be sure to be able to add a Number to a List that contain any subclass from one of the super classes of Number)
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.
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 Numbers 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 Integers 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 Numbers 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 Numbers, 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 Numbers. 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."
List<? super Number> means that the reference type of the variable suggests we have a list of Numbers, Objects or Serializables.
The reason you can't add an Object, is because the compiler does not know WHICH of these classes are in the generic definition of the actual instantiated object, so it only allows you to pass Number or subtypes of Number, like Double, Integer and so on.
Let's say we have a method that returns a List<? super Number>. The creation of the object inside the method is encapsulated from our view, we just can't say if it is something like this:
List<? super Number> returnValue = new LinkedList<Object>();
or
List<? super Number> returnValue = new ArrayList<Number>();
So, the generic type could be Object or Number. In both cases, we would be allowed to add Number, but only in one case we would be allowed to add Object.
You have to distinguish between the reference type and the actual object type in this situation.
There are two angles here: what you can put into a collection and what you can get from a collection, when bounded types are involved.
Let's look at the ? extends Number case first. When a collection with such bounds is defined, what we know is that : every element will have an upper bound as Number. We don't know the exact type (might be an Integer/Long/etc), but we do know, for sure, that its upper bound is Number.
So reading from such a collection gets us a Number. This is the only guaranteed type we can get from it.
Writing to such a collection is prohibited. But why? Didn't I say that while we read - we will always get a Number, so why prohibit writing to it? The situation is slightly more involved here:
List<Integer> ints = ....;
List<? extends Number> numbers = ints;
numbers.add(12D); // add a double in here
If addition would have been allowed into numbers, you could have effectively added a Double in a List of Integers.
Now to your example:
List<? super Number> list = null;
list.add(new Integer(0));
list.add(new Object());
We know about list that it contains a certain supertype of Number, for example Object.
Reading from such a list would get us a certain type X, where X would be a parent of Number. So what would that be? You can't really know. It could be a theoretical MyNumber extends Number, or much simpler: an Object. Since you can't know for sure, the only safe thing to read from that would be the super-type of everything - Object.
What is a bit weird may be :
List<? super String> list = ...;
String s = list.get(0); // fails, compiler does not care that String is final
Writing to it is slightly more complicated, but only slightly. Remember what we know is inside that list: it's a type that Number extends/implements (if it were an interface), so you can always assign a subtype (or Number itself) to that supertype.
Some type X
/ \
|
Number
/ \
|
Some type Y that we an put in here
List<? super Number> is such a List<AncestorOfNumber> where we can implicitely cast each Number to its super type AncestorOfNumber.
Consider this: What generic type needs to be ???? in the following example?
InputStream mystream = ...;
void addTo(List<????> lsb) {
lsb.add(new BufferedInputStream(mystream));
}
List<BufferedInputStream> lb = new ArrayList<>();
List<InputStream> li = new ArrayList<>();
List<Object> lo = new ArrayList<>();
...
{ addTo(lb); addTo(li); addTo(lo); }
The answer: ???? is anything to which we can cast BufferedInputStream, which is that very same or one of its ancestors: ? super BufferedInputStream
May I give a very simple Example.
public void add(List<? super Number> list) {
}
this will allow these calls
add(new LinkedList<Number>());
and everything above Number like
add(new LinkedList<Object>());
but nothing below the hierarchy so not
add(new LinkedList<Double>());
or
add(new LinkedList<Integer>());
So since its not clear for the program to know whether you give a List with Number or Object the compiler cannot allow you to add anything above Number to it.
For example a List would not accept an Object in spite of Object who would accept a Number. But since this is not clear the only valid input would be Number and its sub types.
while reading through this article I got stuck here. I am pasting this from the link. I do not understand the reasoning given for why List<Number> or List<? extends Number> cannot be used here.
public void doStuff( List<Integer> list ) {
list.add(1);
// do stuff
list.get(0);
}
We can generalize this one step further by generalizing the generic parameter:
public void doStuff( List<? super Integer> list ) {
list.add(1);
// do stuff
list.get(0);
}
Some readers might ask why the more intuitively List<Number> can’t be used here. Indeed, we
could try to define the method as taking a List<Number> or a List<? extends Number>, but the first
definition would exclude the possibility for passing in an actual ArrayList<Integer>, while the
second definition would disallow the add() method (as someone may otherwise be passing an
ArrayList<Float> in, and would find an Integer to be between the Floats after the call to doStuff).
In normal Java, yes, an Integer is a Number. But in generics, List<Integer> is not a List<Number>.
To see why, attempt to assign a List<Integer> to a List<Number> and see what happens:
List<Number> numberList = new ArrayList<Integer>(); // not allowed
// This would have been allowed, even though the argument is
// boxed to a `Double`, not a `Integer`.
numberList.add(8.6);
But what about <? extends Number>? Wouldn't that cover a List<Integer>? Yes, but the references loses information about the exact type of Number. What if that was the case?
List<? extends Number> numberList = new ArrayList<Integer>(); // allowed
numberList.add(8.6); // disallowed
The reason is that the List<? extends Number> could be of anything that extends Number, such as List<BigDecimal>. So it must disallow calling the add method (or any method in that class with the generic type parameter as a parameter to that method) (except for null) to maintain type safety.
Java is perhaps a bit confusing because there is a bit of a double standard at work.
First you must consider that both arrays and collections are reference types, i.e. their instances are objects whose data is allocated to heap memory and indicated by a reference pointer.
Both arrays and collections therefore have two types at work: the type of object itself as well as the types of each of the components in the array or collection. To make this concrete, here is an example:
String[] strings = new String[] { "AA", "BB", "CC" };
The type of the object that is created is String[] and the types of all the components is String.
Arrays are covariant which allows the JVM to cast both the object type and the component type together. This is why assignments like this are valid:
Object[] objects = strings;
For arrays, because Object is a supertype of String, then Object[] is also a supertype of String[]. Arrays are covariant.
This does NOT apply to reference types that are not arrays, eg. Collections. Collections are invariant. Therefore, even though a Integer is a subtype of Number, collections are invariant and so List<Integer> is NOT a subtype of List<Number>.
For the first case, accepting a List<Number> only allows a List (ArrayList, LinkedList, ...) of Number elements, not a List of any other type (including Integer). The list has to be specifically typed in the calling function as List<Number> (or ArrayList<Number>, or LinkedList<Number>, or ...). In other words, the type of list is flexible but the generic argument is not.
In the second case, use the "is a" wording and it makes sense. An Integer "is a" Number, but the reverse is not always true. Since the code in the example is assuming all values in use within the function are integers, it must put a limit on the generic that prevents anything less specific than Integer from being passed in.
We need to examinate two things:
What does the wildcard mean in the signature void doStuff(Foo<? super Bar> foo)
What does the wildcard mean inside the method body
Java has only one very simple rule to decide the subtype relation between Foo<A> and Foo<B>: none is a subtype of the other. We say that generic types are invariant, and even if there's a rationale because the Java designers made it this way, from your point of view it's an arbitrary decision.
It's the angle brackets that confuse us, poor developers: we have no problem in accepting that FooBar and FooQoo are not related in any way; but for some reason we need to believe that Foo<Qoo> and Foo<Bar> are. No, that's not the case.
No matter how A and B relate to each other, X<A> and X<B> are not
related.
No matter how A and B relate to each other, X<A> and X<B>
are not related.
No matter how A and B relate to each other, X<A>
and X<B> are not related.
Once you are convinced of the above, please observe this snippet:
List<Double> doubles = ...;
List<Integer> integers = ...;
Number firstDouble = doubles.get(0);
Number firstInteger = integers.get(0);
Calling get(0) on both lists gives us a Number-compatible object. We may want to put the call to get() in a method like getFirstOfList(list) but we just learned that such a method can't exist, because it would accept two totally unrelated types.
This is where wildcards come into play! We observe that calling get() on a List<Number>, a List<Integer>, a List<Double> (and so on) return a Number-compatible object (ie Number or a subtype of it), so there must be a way to express this at the language level. The Java designers gave us the wildcard, which works like this: when you declare
public void doStuff(List<? extends Number> arg);
it has the same effect as declaring the following infinite list:
public void doStuff(List<Number> arg);
public void doStuff(List<Integer> arg);
public void doStuff(List<Double> arg);
public void doStuff(List<Float> arg);
public void doStuff(List<BigDecimal> arg);
...
Without the device of the wildcard you'd have to write one method for each supported list type (which btw is illegal in Java, but that's another story).
The wildcard I used in my example has an upper bound, identified by the extends keyword. The snippet you pasted, instead, adopts a lower bounded wildcard in the method signature (super). Actually it may contain some errors because for example:
you can't pass a List<Integer> to doStuff()
you can only get an Object from list.get(index)
so I will just tell you that the signature
void doStuff(List<? super Number> arg);
stands for the finite list:
void doStuff(List<Number> arg);
void doStuff(List<Object> arg);
and you can put any Number you like in a List<? super Number> but you'll only get() Object's from it.