簡體   English   中英

是列表 <List<String> &gt; Collection的一個實例 <Collection<T> &gt;?

[英]Is List<List<String>> an instance of Collection<Collection<T>>?

我編寫了這個方便的通用函數,用於將集合集合轉換為單個集合:

public static <T> Set<T> makeSet(Collection<Collection<T>> a_collection) {
    Iterator<Collection<T>> it = a_collection.iterator();
    Set<T> result = new HashSet<T>();
    while (it.hasNext()) {
        result.addAll(it.next());
    }
    return result;
}

然后我試着打電話給它:

    List<List<String>> resultLists = ... ;
    Set<String> labelsSet = CollectionsHelper.makeSet(resultLists);

我收到以下錯誤:

<T>makeSet(java.util.Collection<java.util.Collection<T>>) in CollectionsHelper 
cannot be applied to (java.util.List<java.util.List<java.lang.String>>)

現在List 是一個 CollectionString T 那么為什么這不起作用,我該如何解決呢?

你的簽名應該是:

public static <T> Set<T> makeSet(Collection<? extends Collection<T>> coll);

基本上List<S> 不是 List<T>的子類型,因為ST的子類型。 該屬性稱為協方差 ,在Java中, 泛型類型不是協變的 (其他語言如scala包含協變泛型類型)。

你所做的不起作用,因為它應該可以將任何Collection<T>添加到Collection<Collection<T>> ,例如,使用你的簽名,這將是一個有效的實現:

public static <T> Set<T> makeSet(Collection<Collection<T>> coll) {
    coll.add(new HashSet<T>());
    return null;
}

但是后來調用這個方法如下:

List<List<String>> outside = new LinkedList<List<String>>();
makeSet(outside); //actually this line will not compile!
List<String> oops = outside.get(0); //oh dear - it's a HashSet

那么這會導致同樣的問題嗎? 沒有! 原因是編譯器不允許您在未知類型參數化的集合中添加任何內容:

public static <T> Set<T> makeSet(Collection<? extends Collection<T>> coll) {
    coll.add(new HashSet<T>()); //this line will not compile
    return null;
}

首先需要使用通配符,以便您可以執行類似於您想要執行的操作,最好通過如何生成Collection.addAll方法來證明這一點,以便允許List<Number>.addAll(List<Integer>)

boolean addAll(Collection<? extends T> coll)
public static <T> Set<T> makeSet(Collection<? extends Collection<T>> a_collection) {
    Iterator<? extends Collection<T>> it = a_collection.iterator();
    Set<T> result = new HashSet<T>();
    while (it.hasNext()) {
            result.addAll(it.next());
    }
    return result;
}

不,不是。

我會改變聲明

public static <T> Set<T> makeSet(Collection<? extends Collection<T>> a_collection) {
    ....
}

只有當類型參數相同(或使用通配符,因此Collection<String>不是Collection<Object>的子類型時,兩個泛型類型才能是子類型。請檢查泛型教程的子類型部分

這是更廣義問題的專用版本,“ Collection<Circle>是一種Collection<Shape> ?”

答案是(也許是令人驚訝的) 沒有

C ++ FAQ中的C ++上下文中已經說明了這個推理。 這是一般的OO問題,因此同樣的一般推理也適用。

例如,考慮一個替代的Universe,其中Collection<Circle> 一種Collection<Shape> 在這個宇宙中,你可以這樣做:

Collection<Circle> circles = new Collection<Circle>();
Collection<Shape> shapes = circles; // OK, we're in an alternate universe
shapes.Add(new Circle()); // OK, we're adding a circle to a collection of circles
shapes.Add(new Square()); // Asplode!  We just added a square to a collection of circles.

當Square,一個Shape被添加到形狀集合中時會發生什么,這實際上是一個圓圈的集合? 沒有好的答案。

相同的推理適用於Collection<List<T>>Collection<Collection<T>> Collection<List<T>>不是Collection<Collection<T>>因為它不能代替 Collection<Collection<T>> 可以將Queue<T>添加到集合集合中,但不能將其添加到List<T>的集合中。

我幾乎不想發布正確的答案,因為它太丑了,但由於三個頂級答案都錯過了這個,我感到被迫。

public static <T> Set<T> makeSet(
    Collection<? extends Collection<? extends T>> coll)

你讀得對。 兩個“延伸”的。 否則,您不能將List <Integer>和List <Double>放在一起以獲取Set <Number>,這在邏輯上應該是可能的。

一旦你使用嵌套在泛型內部的泛型,事情總是變得討厭。

相反,你可以完全原諒選擇更簡單的答案。 :)只是知道它不會總是在邏輯應該的地方工作。

順便說一句,使用Google Collections ,您可以使用Iterables.concat(...)執行此操作,或者如果需要重復數據刪除,則可以使用ImmutableSet.copyOf(Iterables.concat(...))

這是他開發Java 1.0以簡化所有正在發生的愚蠢的C ++模板的東西。 您添加了5層復雜化,以避免從一組對象中的一個愚蠢的強制轉換為您的特定實例集。 好的,如果你發現你在整個地方投擲並使你的代碼變得丑陋,但實際上我敢打賭這種情況發生在500k行代碼中。 雅,我們可以找到這些技術細節,但是當你開始沿着這條路走下去時,你的代碼真的變得更加可維護了嗎?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM