How to iterate over a mixed list using foreach?
I'm wondering how to iterate over a List with mixed contents using foreach. See the example code below.
public class Gen开发者_开发问答ericsForeach {
class A {
void methodA() {
System.out.println(getClass().getSimpleName() + ": A");
}
}
class B extends A {
void methodB() {
System.out.println(getClass().getSimpleName() + ": B");
}
}
void test() {
List<A> listOfA = new ArrayList<A>();
listOfA.add(new A());
List<B> listOfB = new ArrayList<B>();
listOfB.add(new B());
List<? super A> mixed = new ArrayList<A>();
mixed.addAll(listOfA);
mixed.addAll(listOfB);
Iterator<? super A> it = mixed.iterator();
while (it.hasNext()) {
A item = (A) it.next();
item.methodA();
}
// XXX: this does not work
// for (A item : mixed) {
// item.methodA();
// }
}
public static void main(String[] args) {
new GenericsForeach().test();
}
}
I construct two lists with different, but related, content types A
and B
(B
extends A
). I add the two lists to a 'mixed' list, which I declare to contain <? super A>
types. Since this mixed list is 'consuming' items of type A
(or B
) I applied Bloch's PECS rule (Producer Extends, Consumer Super) to determine that I need <? super A>
here.
So far, so good. But now when I want to iterate over this mixed list, I can only seem to do it with an Iterator<? super A>
, and a cast A item = (A) it.next()
. When I try to use a foreach loop (see commented-out code), no joy:
Type mismatch: cannot convert from element type capture#8-of ? super GenericsForeach.A to GenericsForeach.A
Eclipse even helpfully offers to
Change type of 'item' to '? super A'
but this results in disaster:
for (? super A item : mixed) {
item.methodA();
}
So I don't know. Eclipse doesn't seem to know. Does anybody else here know if this is possible, and if it's not, why not?
You want just List<A>
for mixed
. My reasoning:
- you want to be able to add items which are of type
A
, so it can't beList<? extends A>
- that would includeList<B>
, which you can't add anA
to. - you want to be able to guarantee that items which you fetch are of type
A
, so it can't beList<? super A>
as that could be aList<Object>
containing non-A elements.
So you end up with:
List<A> mixed = new ArrayList<A>();
mixed.addAll(listOfA);
mixed.addAll(listOfB);
for (A item : mixed) {
item.methodA();
}
Everyone here is correct. You want use List<A>
.
But generics and assignments can be confusing, so a little more explanation is in order.
First, the problem you may have found is that you can't do this: List<A> = new List<B>()
. The compiler won't let you assign a sub-type in to a super-type listing using generics. This is a little confusing, but it prevents problems with type mis-matches. (More detail can be found here: http://java.sun.com/docs/books/tutorial/java/generics/subtyping.html.) The correct terminology for this is List<? extends A> = new List<B>()
. This tells the compiler that your assignment is legal.
At the same time, this syntax can confuse you to believing that <? extends A>
means that all elements in this variable extend A. This isn't true - the syntax just a way to inform the compiler of legal assignments.
So, you want to use List<A> = new List<A>
and then assign the elements to List<A>
using addAll()
. This is legal because the method addAll checks to make sure each element is valid before pushing it to the collection.
精彩评论