2

while reading through this article I got stuck here. I am pasting this from the link. I do not understand the reasoning given for why List<Number> or List<? extends Number> cannot be used here.

public void doStuff( List<Integer> list ) {     
    list.add(1);
    // do stuff
    list.get(0);    
}

We can generalize this one step further by generalizing the generic parameter:

public void doStuff( List<? super Integer> list ) {     
    list.add(1);
    // do stuff
    list.get(0);    
}

Some readers might ask why the more intuitively List<Number> can’t be used here. Indeed, we could try to define the method as taking a List<Number> or a List<? extends Number>, but the first definition would exclude the possibility for passing in an actual ArrayList<Integer>, while the second definition would disallow the add() method (as someone may otherwise be passing an ArrayList<Float> in, and would find an Integer to be between the Floats after the call to doStuff).

ruakh
  • 175,680
  • 26
  • 273
  • 307
brain storm
  • 30,124
  • 69
  • 225
  • 393

4 Answers4

5

In normal Java, yes, an Integer is a Number. But in generics, List<Integer> is not a List<Number>.

To see why, attempt to assign a List<Integer> to a List<Number> and see what happens:

List<Number> numberList = new ArrayList<Integer>();  // not allowed
// This would have been allowed, even though the argument is
// boxed to a `Double`, not a `Integer`.
numberList.add(8.6); 

But what about <? extends Number>? Wouldn't that cover a List<Integer>? Yes, but the references loses information about the exact type of Number. What if that was the case?

List<? extends Number> numberList = new ArrayList<Integer>();  // allowed
numberList.add(8.6);  // disallowed

The reason is that the List<? extends Number> could be of anything that extends Number, such as List<BigDecimal>. So it must disallow calling the add method (or any method in that class with the generic type parameter as a parameter to that method) (except for null) to maintain type safety.

rgettman
  • 176,041
  • 30
  • 275
  • 357
  • does it mean if `employee extends person {}`, I cannot have List p = new ArrayList` – brain storm Oct 24 '13 at 22:30
  • 1
    That is correct. Such a list would let you add a `Contractor` (that also extends `Person`), even though the list is actually a `List`. So, that is disallowed. – rgettman Oct 24 '13 at 22:32
  • assuming no body extends person except employee, is that still illegal – brain storm Oct 24 '13 at 22:33
  • It would still be illegal, because Java can't make any assumptions that no other class extends another class. Besides, you _could_ make a `Contractor` class in the future, and the type safety must hold even then. – rgettman Oct 24 '13 at 22:36
  • by saying `? super Integer`, do we mean any object whose superclass is integer correct..and `? extends Number`, do we mean any object that extends Number class.. – brain storm Oct 24 '13 at 22:41
  • Close. `? super Integer` means that the generic type parameter is `Integer` or a superclass thereof -- it could be `Integer`, `Number`, or `Object`. `? extends Number` could be any class that extends `Number` (or `Number` itself). – rgettman Oct 24 '13 at 22:44
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/39952/discussion-between-user1988876-and-rgettman) – brain storm Oct 24 '13 at 22:46
1

Java is perhaps a bit confusing because there is a bit of a double standard at work.

First you must consider that both arrays and collections are reference types, i.e. their instances are objects whose data is allocated to heap memory and indicated by a reference pointer.

Both arrays and collections therefore have two types at work: the type of object itself as well as the types of each of the components in the array or collection. To make this concrete, here is an example:

String[] strings = new String[] { "AA", "BB", "CC" };

The type of the object that is created is String[] and the types of all the components is String.

Arrays are covariant which allows the JVM to cast both the object type and the component type together. This is why assignments like this are valid:

Object[] objects = strings;

For arrays, because Object is a supertype of String, then Object[] is also a supertype of String[]. Arrays are covariant.

This does NOT apply to reference types that are not arrays, eg. Collections. Collections are invariant. Therefore, even though a Integer is a subtype of Number, collections are invariant and so List<Integer> is NOT a subtype of List<Number>.

scottb
  • 9,908
  • 3
  • 40
  • 56
0

For the first case, accepting a List<Number> only allows a List (ArrayList, LinkedList, ...) of Number elements, not a List of any other type (including Integer). The list has to be specifically typed in the calling function as List<Number> (or ArrayList<Number>, or LinkedList<Number>, or ...). In other words, the type of list is flexible but the generic argument is not.

In the second case, use the "is a" wording and it makes sense. An Integer "is a" Number, but the reverse is not always true. Since the code in the example is assuming all values in use within the function are integers, it must put a limit on the generic that prevents anything less specific than Integer from being passed in.

seawolf
  • 2,147
  • 3
  • 20
  • 37
-1

We need to examinate two things:

  1. What does the wildcard mean in the signature void doStuff(Foo<? super Bar> foo)
  2. What does the wildcard mean inside the method body

Java has only one very simple rule to decide the subtype relation between Foo<A> and Foo<B>: none is a subtype of the other. We say that generic types are invariant, and even if there's a rationale because the Java designers made it this way, from your point of view it's an arbitrary decision.

It's the angle brackets that confuse us, poor developers: we have no problem in accepting that FooBar and FooQoo are not related in any way; but for some reason we need to believe that Foo<Qoo> and Foo<Bar> are. No, that's not the case.

No matter how A and B relate to each other, X<A> and X<B> are not related.

No matter how A and B relate to each other, X<A> and X<B> are not related.

No matter how A and B relate to each other, X<A> and X<B> are not related.

Once you are convinced of the above, please observe this snippet:

List<Double> doubles = ...;
List<Integer> integers = ...;

Number firstDouble = doubles.get(0);
Number firstInteger = integers.get(0);

Calling get(0) on both lists gives us a Number-compatible object. We may want to put the call to get() in a method like getFirstOfList(list) but we just learned that such a method can't exist, because it would accept two totally unrelated types.

This is where wildcards come into play! We observe that calling get() on a List<Number>, a List<Integer>, a List<Double> (and so on) return a Number-compatible object (ie Number or a subtype of it), so there must be a way to express this at the language level. The Java designers gave us the wildcard, which works like this: when you declare

public void doStuff(List<? extends Number> arg);

it has the same effect as declaring the following infinite list:

public void doStuff(List<Number> arg);
public void doStuff(List<Integer> arg);
public void doStuff(List<Double> arg);
public void doStuff(List<Float> arg);
public void doStuff(List<BigDecimal> arg);
...

Without the device of the wildcard you'd have to write one method for each supported list type (which btw is illegal in Java, but that's another story).

The wildcard I used in my example has an upper bound, identified by the extends keyword. The snippet you pasted, instead, adopts a lower bounded wildcard in the method signature (super). Actually it may contain some errors because for example:

  1. you can't pass a List<Integer> to doStuff()
  2. you can only get an Object from list.get(index)

so I will just tell you that the signature

void doStuff(List<? super Number> arg);

stands for the finite list:

void doStuff(List<Number> arg);
void doStuff(List<Object> arg);

and you can put any Number you like in a List<? super Number> but you'll only get() Object's from it.

Raffaele
  • 20,627
  • 6
  • 47
  • 86