<? super E> and <? extends E> for List
Hav开发者_开发技巧ing the following simple class structure:
class A {
}
class B extends A {
}
class C extends B {
}
I'm creating an ArrayList to keep objects of the earlier created classes:
List<? extends A> list1 = new ArrayList<A>();
List<? extends B> list2 = new ArrayList<B>();
List<? extends C> list3 = new ArrayList<C>();
List<? super A> list4 = new ArrayList<A>();
List<? super B> list5 = new ArrayList<B>();
List<? super C> list6 = new ArrayList<C>();
To each of those lists I'm trying to add 1 object of each earlier created class: A,B,C. The only possible combination is:
adding object of class A,B,C to list4
adding object of class B and C to list5
adding object of class C to list list6. The rest of the tries gives compiler errors, such us:
The method add(capture#1-of ? extends A) in the type List is not applicable for the arguments (A)
Why can't I add any object of class A,B,C to list1/2/3? Why e.g. list4 accepts objects of classes A,B,C if they are supposed to be a super class of class A, as the list4 is defined?
"? extends A" means "some type derived from A (or A itself)". So for instance, a List<ByteArrayOutputStream>
is compatible with List<? extends OutputStream>
- but you shouldn't be able to add a FileOutputStream
to such a list - it's meant to be a List<ByteArrayOutputStream>
! All you know is that anything you fetch from the list will be an OutputStream
of some kind.
"? super A" means "some type which is a superclass of A (or A itself)". So for instance, a List<OutputStream>
is compatible with List<? super ByteArrayOutputStream>
. You can definitely add a ByteArrayOutputStream
to such a list - but if you fetch an item from the list, you can't really guarantee much about it.
See Angelika Langer's Generics FAQ for much more information.
The type definition List<? extends A>
is not usable for a mutable List - the explanation given in Java generics Java Generics Pdf is
The add() method takes arguments of type E, the element type of the collection. When the actual type parameter is ?, it stands for some unknown type. Any parameter we pass to add would have to be a subtype of this unknown type. Since we don’t know what type that is, we cannot pass anything in.
However, when the typedef is List<? super A>
then the type parameter ? is implicitly typed.
List<? extends A> list1
It is a list, whose type element could be any unknown subclass of A. For example, it could be a D subclass. Therefore, you can't add anything to it, it could be wrong...
List<? super A> list4
It is a list, whose type element could be A, or a superclass of A (does not exist in that case, except Object). Therefore, you can add A objects to it, or any subclass of A such as B or C.
It doesn't work that way.
You should use <? extends T>
when you create function which argument is collection of unknown subtype of some type, and you want to fetch objects from that collection:
int sum(Collection<? extends Integer> collection) {
for (Integer : collection) {
// do sth
}
// ...
}
You cannot add new items to this collection, because you don't know which concrete type is this collection holding. All you know is that that type extends Integer
.
You use <? super T>
when you want to add new items of type T
to collection and return that collection, but then you cannot guarantee what you can retrieve from it and you have to cast result of get()
or check its type. You can safely add items of type T
and subtypes, and retrieve items of type T
.
List<? super B>
allows you to use Lists of any supertype of B, i.e. list5 = new ArrayList<A>();
or list5 = new ArrayList<Object>();
You can safely add B (and subtypes) to every list that use supertypes of B, but you can not add any supertype of B. Imagine this:
public void myAdd(List<? super B> lst) {
lst.add(new Object()) //this is a supertype of B (compile time error)
}
...
ArrayList<A> list = new ArrayList<A>();
myAdd(list); //tries to add Object to a list of type A
精彩评论