[英]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.