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
是一个 Collection
, String
是 T
那么为什么这不起作用,我该如何解决呢?
public static Set makeSet(Collection extends Collection > a_collection) { Iterator extends Collection > it = a_collection.iterator(); Set result = new HashSet (); while (it.hasNext()) { result.addAll(it.next()); } return result; }
你的签名应该是:
public static Set makeSet(Collection extends Collection > coll);
基本上List
不是 List
的子类型,因为S
是T
的子类型。 该属性称为协方差 ,在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 extends Collection > coll) { coll.add(new HashSet ()); //this line will not compile return null; }
首先需要使用通配符,以便您可以执行类似于您想要执行的操作,最好通过如何生成Collection.addAll
方法来certificate这一点,以便允许List
:
boolean addAll(Collection extends T> coll)
不,不是。
我会改变声明
public static Set makeSet(Collection extends 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 extends Collection extends T>> coll)
你看对了。 两个“?延伸”的。 否则,您不能将List
一旦你将generics嵌入到generics中,事情就会变得很糟糕。
相反,你可以完全原谅选择更简单的答案。 :)只要知道它在逻辑上不应该总是起作用。
顺便说一句,使用Google Collections ,您可以使用Iterables.concat(...)
执行此操作,或者如果需要重复数据删除,则可以使用ImmutableSet.copyOf(Iterables.concat(...))
。
这是他开发Java 1.0以简化所有正在发生的愚蠢的C ++模板的东西。 您添加了5层复杂化,以避免从一组对象中的一个愚蠢的强制转换为您的特定实例集。 好的,如果你发现你正在整个地方投射并使你的代码变得丑陋,但实际上我敢打赌这种情况大约发生在500k行代码中。 雅,我们能找到这些技术细节是件好事,但是当你开始沿着这条路走下去时,你的代码是否真的变得更易于维护?