为什么我不能将A的实例添加到声明为Set <! - 的集合中?延伸A - >?

时间:2014-01-20 11:42:56

标签: java generics

我有:

class Document { ... }

class DocumentCluster extends Document { ... }

我正试图用这种方式定义一组文档:

Set<? extends Document> docs = new HashSet<Document>();

但是,当我尝试将文档插入到我的设置中时:

docs.add(d);

我得到了:

The method add(capture#10-of ? extends Document) in the type Set<capture#10-of ? extends Document> is not applicable for the arguments (Document)

我做错了什么?

2 个答案:

答案 0 :(得分:5)

因为它可能只允许B类型的对象。

一个经典的问题,回答了一百万次。非直观,但也不是Java的设计缺陷。

以下是经典示例:让A成为Fruit

我可以将Apple加入Set<? extends Fruit>吗?

,因为它可能是Set<Banana>,显然不得包含苹果。

? extends Fruit一些特定类型的水果“任何种类的水果”。然后它将是Set<Fruit>,它确实可以采取任何种类的水果。

根据经验:

  • ? super Fruit方便。这需要“至少”水果
  • 获取? extends Fruit很方便。它可能只返回一种水果,但它们都是水果。

考虑这种方法:

public static double computeAverage(Collection<? extends Number> col) {
    double sum = 0.;
    for (Number n : col) {
      sum += n.doubleValue();
    }
    return sum / n.size();
}

此方法可以List<Integer>一起使用。因为它Double放入列表中,但只将Number个对象取出。

public static void put(Collection<? super Number> col, Number n) {
    col.put(n);
}

在这里,我们有相反的情况。我们需要一个接受任意数字的列表(否则,我们无法将非特定数字放入其中)。但是,它可能会接受更多:

put(new List<Object>(), (Double) 1.);

是有效的,因为我可能将双打放入对象列表中。

但编译器正确会阻止put( new List<Integer>(), (Double) 1.)

它可能比那更麻烦:

public static <I,O> void transfer(Collection<? extends I> input,
                                  Collection<? super O> output,
                                  Converter<? super I, ? extends O> converter) {
  for (I inobj : input) {
    O outobj = converter.convert(inobj);
    output.put(outobj);
  }
}

但是编译器可能无法每次为您自动找出IO

答案 1 :(得分:0)

无法添加

Set<? extends A>,因为您没有准确添加到集合的类型,只是添加了扩展A的SOMETHING。您可以保证使用{{1}时你会得到一个扩展A的类,但是当你使用get()因为你不知道确定类型时,就无法添加到集合中

如果您想利用多态性,请使用add()。这将完全相同 - 任何类型兼容的成员,即Set<A>的扩展名,都可以放在A中。

编辑: @ Anony-Mousse用一个例子更好地解释了这一点。