代码之家  ›  专栏  ›  技术社区  ›  Adriaan Koster

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

  •  1
  • Adriaan Koster  · 技术社区  · 16 年前

    我想知道如何使用foreach遍历包含混合内容的列表。请参阅下面的示例代码。

    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 ( 延伸 )我将这两个列表添加到一个“混合”列表中,我声明其中包含 <? super A> 类型。因为这个混合列表正在“消耗”类型的项 (或) )我应用了bloch的pecs规则(producer extends,consumer super)来确定我需要 <?超级A和GT; 在这里。

    到目前为止,还不错。但现在当我想遍历这个混合列表时,我只能用 Iterator<? super A> 和演员 A item = (A) it.next() . 当我尝试使用foreach循环(请参阅注释掉的代码)时,没有乐趣:

    类型不匹配:无法从元素类型捕获转换8-of?超级通用foreach.a到通用foreach.a

    eclipse甚至乐于提供

    是否将“项”的类型更改为?超级A

    但这会带来灾难:

    for (? super A item : mixed) {
        item.methodA();
    }
    

    所以我不知道。日食似乎不知道。这里还有人知道这是否可能,如果不可能,为什么不知道?

    2 回复  |  直到 16 年前
        1
  •  11
  •   Jon Skeet    16 年前

    你只想 List<A> 对于 mixed . 我的理由是:

    • 您希望能够添加类型为 A ,所以不可能是 List<? extends A> -包括 List<B> ,无法添加 去。
    • 你希望能够保证 取来 属于类型 ,所以不可能是 List<? super A> 因为那可能是 List<Object> 包含非a元素的。

    所以你的结局是:

    List<A> mixed = new ArrayList<A>();
    mixed.addAll(listOfA);
    mixed.addAll(listOfB);
    
    for (A item : mixed) {
      item.methodA();
    }
    
        2
  •  1
  •   Jonathan B    16 年前

    这里的每个人都是对的。你要用 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> 然后将元素分配给 列表<a> 使用 addAll() . 这是合法的,因为addall方法在将每个元素推送到集合之前检查以确保其有效。