5

From http://www.angelikalanger.com/GenericsFAQ/FAQSections/TypeArguments.html#FAQ103:

A wildcard with a lower bound looks like " ? super Type " and stands for the family of all types that are supertypes of Type , type Type being included. Type is called the lower bound .

So why

ArrayList<? super Number> psupn1 = new ArrayList<Number>();
psupn1.add(new Double(2));

compiled?

Double is not supertype of Number but subclass of Number...

Edit 1:

    ArrayList<? super Number> pextn1 = new ArrayList<Number>();
    psupn1.add(new Integer(2));
    psupn1.add(new Double(2));
    psupn1.add(new Float(2));
    for(Number n : psupn1){ // [Invalid] Number should be change to
    // Object even if I can only add subtype of Number??

    }
JohnJohnGa
  • 15,446
  • 19
  • 62
  • 87
  • 2
    This might help: [http://stackoverflow.com/questions/2723397/java-generics-what-is-pecs][1] [1]: http://stackoverflow.com/questions/2723397/java-generics-what-is-pecs – James 'Cookie' Cook Nov 10 '11 at 16:52

4 Answers4

6

You can add a Double to that, because whatever the type parameter E is, it's guaranteed to be either Number or a supertype... which means you can definitely convert from Double to E. You wouldn't be able to do:

Number x = psupn1.get(0);

though.

Think about it, and try to create lists which would logically break this. For example, you can't use:

// Invalid
ArrayList<? super Number> psupn1 = new ArrayList<Integer>();
psupn1.add(new Double(2));

because Integer isn't either Number or a supertype - it's a subclass. You can write:

// Valid
ArrayList<? extends Number> psupn1 = new ArrayList<Integer>();

... because that's the other way round. At that point you can write:

Number x = psupn1.get(0);

because any element in the list is guaranteed to be convertible to Number. It's all about which way the conversions are required - to the generic type parameter or from it.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
5

Maurice Naftalin and Philip Wadler explain it best in Java Generics and Collections:

The Get and Put Principle: use an extends wildcard when you only get values out of a structure, use super wildcard when you only put values into a structure, and don't use a wildcard when you both get and put.

Julien Chastang
  • 17,592
  • 12
  • 63
  • 89
0

For any type X, you can only retrieve X from List<? extends X> and only insert X into List<? super X>. But you can both insert and retrieve X to/from List<X>.

Here you can insert any Number into this instance of List<? super Number>, and of course all of your examples, new Integer(2), new Double(2) etc. are instances of Number, so they can be put into the list.

But if you call .get() on this list you cannot assume it contain only numbers. To see why note that this is legal java code:

    List<Serializable> serializables = new ArrayList<Serializable>();
    serializables.add("You know String implements Serializable!");
    List<? super Number> list = serializables;
    Object o = list.get(0);  // Object is your best guess about the result's type

Also note that there is nothing magical in .get() and .add(). Actually all this happens because if you inspect the definition of java.util.List<E> you can see that .get() returns an E while add() takes an E as a parameter.

Saintali
  • 4,482
  • 2
  • 29
  • 49
0

What List<? super Number> says is: we guarantee you can put a Number, or any subclass of Number, into this List. It doesn't make any guarantees about what you get out of it. Consider:

void <T> addToList(List<? super T> list, T... things) {
    for (T t: things) {
        list.add(t);
    }
}

void <T> printList<List<? super T> list) {
    for (T t: list) { // doesn't compile
        System.out.println(t);
    }

    for (Object o: list) { // just fine
        System.out.println(o);
    }
}

public static void main(String[] args) {
    List<Object> objects = new ArrayList<Object>();

    // 1, 2.0f, 3.0 autoboxed to Integer, Float, Double
    addToList(objects, "a string", new Object(), 1, 2.0f, 3.0); // just fine
    printList(objects);

    List<Number> numbers = new ArrayList<Number>();
    addToList(numbers, "a string", new Object(), 1, 2.0f, 3.0); // doesn't compile
    addToList(numbers, 1, 2.0f, 3.0); // just fine
    printList(numbers);

    List<Integer> ints = new ArrayList<Integer>();
    addToList(ints, 1, 2.0f, 3.0); // doesn't compile
    addToList(ints, 1, 2, 3); // just fine
    printList(ints);
}
David Moles
  • 48,006
  • 27
  • 136
  • 235