[英]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
是一個 Collection
, String
是 T
那么為什么這不起作用,我該如何解決呢?
你的簽名應該是:
public static <T> Set<T> makeSet(Collection<? extends Collection<T>> coll);
基本上List<S>
不是 List<T>
的子類型,因為S
是T
的子類型。 該屬性稱為協方差 ,在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;
}
這是更廣義問題的專用版本,“ 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.