我想知道如何使用foreach迭代包含混合内容的List。请参阅下面的示例代码。
public class GenericsForeach {
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();
}
}
我构建了两个列表,其中包含不同但相关的内容类型A
和B
(B
extends A
)。我将这两个列表添加到“混合”列表中,我声明它包含<? super A>
类型。由于此混合列表是'消费'类型为A
(或B
)的项目,因此我应用了Bloch的PECS规则(Producer Extends,Consumer Super)来确定我需要<? super A>
。
到目前为止,这么好。但是现在当我想迭代这个混合列表时,我似乎只能使用Iterator<? super A>
和强制转换A item = (A) it.next()
。当我尝试使用foreach循环(参见注释掉的代码)时,没有快乐:
类型不匹配:无法转换元素类型捕获#8-of? super GenericsForeach.A to GenericsForeach.A
Eclipse甚至提供了
将'item'的类型更改为'?超级A'
但这会导致灾难:
for (? super A item : mixed) {
item.methodA();
}
所以我不知道。 Eclipse似乎不知道。这里有没有人知道这是否可能,如果不是,为什么不呢?
答案 0 :(得分:11)
List<A>
只需要mixed
。我的推理:
A
类型的项目,因此不能List<? extends A>
- 包括List<B>
,您无法添加A
{1}} to。A
类型,因此它不能是List<? super A>
,因为它可能是{{1}包含非A元素。所以你最终得到:
List<Object>
答案 1 :(得分:1)
这里的每个人都是正确的。您想要使用List<A>
。
但是泛型和作业可能令人困惑,因此需要更多解释。
首先,您可能发现的问题是您无法执行此操作:List<A> = new List<B>()
。编译器不允许您使用泛型将子类型分配到超类型列表中。这有点令人困惑,但它可以防止类型不匹配的问题。 (更多细节可以在这里找到:http://java.sun.com/docs/books/tutorial/java/generics/subtyping.html。)正确的术语是List<? extends A> = new List<B>()
。这告诉编译器您的作业是合法的。
同时,这种语法会让您误以为<? extends A>
意味着此变量中的所有元素都扩展为A.这不是真的 - 语法只是告知编译器合法分配的一种方式。
因此,您要使用List<A> = new List<A>
,然后使用List<A>
将元素分配给addAll()
。这是合法的,因为addAll方法会在将每个元素推送到集合之前检查以确保每个元素都有效。