2

I have:

class Document { ... }

class DocumentCluster extends Document { ... }

And I'm trying to define a set of documents this way:

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

However, when I'm trying to insert a document to my set:

docs.add(d);

I'm getting:

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

What am I doing wrong?

Shmoopy
  • 5,334
  • 4
  • 36
  • 72

2 Answers2

5

Because it might only allow objects of type B.

A classic question, answered a million of times. Non-intuitive, but also not a design bug of Java.

Here is the classic example: let A be Fruit.

Can I put an Apple into a Set<? extends Fruit>?

No, because it could be a Set<Banana> which obviously must not contain apples.

? extends Fruit says some specific kind of fruit, and not "any kind of fruit". Then it would be a Set<Fruit> which indeed can take any kind of Fruit.

As a rule of thumb:

  • when putting ? super Fruit is convenient. It takes "at least" fruits
  • when getting ? extends Fruit is convenient. It may return only one kind of fruit, but they will all be fruit.

Consider this method:

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

This method can work with List<Integer>. Because it does not put Double into the list, but only takes Number objects out.

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

Here, we have the opposite case. We need a list that accepts arbitrary numbers (otherwise, we could not put the unspecific number in it). It may accept more, though:

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

is valid, because I may put doubles into a list of Objects.

But the compiler correctly prevents put( new List<Integer>(), (Double) 1.).

It can get much messier than that:

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);
  }
}

But the compiler may be unable to figure out I and O automatically for you every time.

Has QUIT--Anony-Mousse
  • 76,138
  • 12
  • 138
  • 194
0

Set<? extends A> can't be added to because you don't exactly what type you are adding to the collection, only that you are adding SOMETHING that extends A. You can guarentee that when you use get() you will get a class that extends A, but when you use add() because you don't know the definitive type, you can't add to the collection

If you want to take advantage of polymorphism, use Set<A>. This will do the exact same thing - any member that is type compatible, ie, extends, from A can be placed in the Set<K>.

EDIT: @Anony-Mousse explained this much better with an example.

Dan
  • 10,282
  • 2
  • 37
  • 64