繁体   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