2010-02-26 67 views
1

我想知道如何迭代使用foreach混合內容的列表。請參閱下面的示例代碼。如何使用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(); 
    } 
} 

我建造兩個清單不同,但相互關聯的內容類型ABB延伸A)。我將這兩個列表添加到「混合」列表中,我聲明它包含<? super A>類型。由於這個混合列表是A(或B)類型的'消費'項目,我應用了Bloch的PECS規則(Producer Extends,Consumer Super)來確定我需要<? super A>

到目前爲止,這麼好。但是現在當我想要遍歷這個混合列表時,我只能用Iterator<? super A>和一個演員A item = (A) it.next()來完成它。當我嘗試使用foreach循環(請參閱註釋掉的代碼)時,沒有快樂:

類型不匹配:無法從元素類型捕獲轉換#8 - 超級GenericsForeach.A到GenericsForeach.A

的Eclipse甚至幫忙,提供了

更改類型 '項' 到「嗎?超A」

但這會導致災難:

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

所以我不知道。 Eclipse似乎並不知道。這裏有其他人知道這是否可能,如果不是,爲什麼不呢?

回答

11

你想要List<A>mixed。我的理由是:

  • 你希望能夠增加它們A類型的項目,所以它不能List<? extends A> - 這將包括List<B>,你不能添加到A
  • 要能夠保證你獲取項目A類型,因此它不能作爲List<? super A>可能是一個List<Object>不含A元素。

所以你最終獲得:這裏

List<A> mixed = new ArrayList<A>(); 
mixed.addAll(listOfA); 
mixed.addAll(listOfB); 

for (A item : mixed) { 
    item.methodA(); 
} 
+1

我同意..想說同樣的話,但你對我來說太快:) – Fortega 2010-02-26 15:14:35

+0

我希望它會比這更復雜...( - ;謝謝! – 2010-02-26 16:25:21

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>,然後使用addAll()將元素分配到List<A>。這是合法的,因爲方法addAll會在將其推送到集合之前檢查每個元素是否有效。