如何使用foreach迭代混合列表?

时间:2010-02-26 15:08:17

标签: java generics foreach

我想知道如何使用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();
    }
}

我构建了两个列表,其中包含不同但相关的内容类型ABB 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似乎不知道。这里有没有人知道这是否可能,如果不是,为什么不呢?

2 个答案:

答案 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方法会在将每个元素推送到集合之前检查以确保每个元素都有效。