List <List >是Collection <Collection >的实例吗?

我编写了这个方便的通用函数,用于将集合集合转换为单个集合:

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

然后我试着打电话给它:

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

我收到以下错误:

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

现在List 是一个 CollectionString T 那么为什么这不起作用,我该如何解决呢?

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

你的签名应该是:

 public static  Set makeSet(Collection> coll); 

基本上List 不是 List的子类型,因为ST的子类型。 该属性称为协方差 ,在Java中, generics类型不是协变的 (其他语言,如scala包含协变generics类型)。

你做了什么没有用,因为它应该可以将任何Collection添加到Collection> ,例如,使用你的签名,这将是一个有效的实现:

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

但是后来调用这个方法如下:

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

那么这会导致同样的问题吗? 没有! 原因是编译器不允许您在未知类型参数化的集合中添加任何内容:

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

首先需要使用通配符,以便您可以执行类似于您想要执行的操作,最好通过如何生成Collection.addAll方法来certificate这一点,以便允许List.addAll(List)

 boolean addAll(Collection coll) 

不,不是。

我会改变声明

 public static  Set makeSet(Collection> a_collection) { .... } 

只有当类型参数相同(或使用通配符,因此Collection不是Collection的子类型时,两个generics类型才能是子类型。请检查generics教程的子类型部分 。

这是更广义问题的专用版本,“ Collection是一种Collection ?”

答案是(也许是令人惊讶的) 没有

这个推理在C ++ FAQ中的C ++上下文中得到了很好的说明。 这是一般的OO问题,因此同样的一般推理也适用。

例如,考虑一个替代的Universe,其中Collection 一种Collection 。 在这个宇宙中,你可以这样做:

 Collection circles = new Collection(); Collection 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>Collection>Collection>不是Collection>因为它不能代替 Collection> 。 可以将Queue添加到集合集合中,但不能将其添加到List的集合中。

我几乎不想发布正确的答案,因为它太丑了,但由于三个顶级答案都错过了这个,我感到被迫。

 public static  Set makeSet( Collection> coll) 

你看对了。 两个“?延伸”的。 否则,您不能将List 和List 放在一起以获取Set ,这在逻辑上应该是可能的。

一旦你将generics嵌入到generics中,事情就会变得很糟糕。

相反,你可以完全原谅选择更简单的答案。 :)只要知道它在逻辑上不应该总是起作用。

顺便说一句,使用Google Collections ,您可以使用Iterables.concat(...)执行此操作,或者如果需要重复数据删除,则可以使用ImmutableSet.copyOf(Iterables.concat(...))

这是他开发Java 1.0以简化所有正在发生的愚蠢的C ++模板的东西。 您添加了5层复杂化,以避免从一组对象中的一个愚蠢的强制转换为您的特定实例集。 好的,如果你发现你正在整个地方投射并使你的代码变得丑陋,但实际上我敢打赌这种情况大约发生在500k行代码中。 雅,我们能找到这些技术细节是件好事,但是当你开始沿着这条路走下去时,你的代码是否真的变得更易于维护?