3

This following is from generics tutorials:

Say class R extends S,

public void addR(List<? extends S> s) {
    s.add(0, new R()); // Compile-time error!
}

You should be able to figure out why the code above is disallowed. The type of the second parameter to s.add() is ? extends S -- an unknown subtype of S. Since we don't know what type it is, we don't know if it is a supertype of R; it might or might not be such a supertype, so it isn't safe to pass a R there.

I have read it a few times but still I don't quite understand why the following is an error

Given the List.add()'s signature

void add(int index, E element)

isn't it equivalent to

void add(int index, <? extends S> element) // just to explain the idea, not a valid syntax

why is it an error call add(0, new R()) R being an S?

Jeff Axelrod
  • 27,676
  • 31
  • 147
  • 246
Murali
  • 1,495
  • 2
  • 15
  • 28

2 Answers2

4

Here's what the text in italics is referring to:

The parameter s, of type List<? extends S>, could be not just an instance of List<S> or List<R>, but also List<T> where T extends S. In that case, even if R also extends S, R does not necessarily extend T (they could be, e.g. siblings in the class hierarchy). Since you can only put a value of type T in such a collection, the compiler can't guarantee at compile time that putting an R there would be safe.

To give a more concrete example, you can't add a Double to a List<? extends Number>, even though Double extends Number! That's because a variable of type List<? extends Number> could, for example, be assigned a List<Integer> at runtime, and adding a Double to such a list is not allowed.

In fact, you can't actually call the add method of a list declared to be List<? extends S>, because at runtime the wildcard could always represent some subtype of S that isn't a superclass of the thing you want to add. You can however read from such a list, since it's guaranteed that the wildcard is a subtype of S, and therefore can be assigned to a variable of type S:

public S getElement(List<? extends S> s) {
  S result = s.get(0);
  return result;
}

This general idea is referred to as PECS (producer-extends, consumer-super). Chapter 5 of Effective Java (conveniently enough, it's the sample chapter you can download from the book's website) has more to say about this and other subtleties of generics.

Phil
  • 4,767
  • 1
  • 25
  • 21
  • 1
    Wait T and R being siblings in class hierarchy and also being an S, why is it that you can only put a T in such a collection but not R? Thanks for the link to Effective Java. – Murali Dec 04 '09 at 07:12
  • I added a more concrete example. Hopefully that helps a bit. – Phil Dec 04 '09 at 07:28
  • So what if it contains a List and a List? It is a heterogeneous List like the pre-JDK 1.4 List that is unparameterized. The get would still work fine since you are assigning to a Number type variable. – Murali Dec 04 '09 at 08:16
  • Good explanation by Phil! The parameter type might be declared as List extends S>, but the actual value passed to the method at runtime will always be either a List, List, List or List. That's why the compiler is not convinced that adding a new R() to this List is a good idea,. – Adriaan Koster Dec 04 '09 at 09:57
  • "you can't actually call the add method" you can always call it and pass `null` – newacct Mar 30 '12 at 05:43
0

Figured this would be the simplest explanation

Class structure:

public class List<? extends S> {}
public class S {}
public class R extends S {}
public class T extends R {}

Code usage:

List<T> list = new List<T>();

in this case the following would be invalid:

list.add(new R());
Randyaa
  • 2,935
  • 4
  • 36
  • 49