What is a difference between <? super E> and <? extends E>?
Asked Answered
G

10

190

What is the difference between <? super E> and <? extends E>?

For instance when you take a look at class java.util.concurrent.LinkedBlockingQueue there is the following signature for the constructor:

public LinkedBlockingQueue(Collection<? extends E> c)

and for one for the method:

public int drainTo(Collection<? super E> c)
Gooch answered 2/9, 2009 at 14:50 Comment(0)
H
218

The first (<? super E>) says that it's "some type which is an ancestor (superclass) of E"; the second (<? extends E>) says that it's "some type which is a subclass of E". (In both cases E itself is okay.)

So the constructor uses the ? extends E form so it guarantees that when it fetches values from the collection, they will all be E or some subclass (i.e. it's compatible). The drainTo method is trying to put values into the collection, so the collection has to have an element type of E or a superclass.

As an example, suppose you have a class hierarchy like this:

Parent extends Object
Child extends Parent

and a LinkedBlockingQueue<Parent>. You can construct this passing in a List<Child> which will copy all the elements safely, because every Child is a parent. You couldn't pass in a List<Object> because some elements might not be compatible with Parent.

Likewise you can drain that queue into a List<Object> because every Parent is an Object... but you couldn't drain it into a List<Child> because the List<Child> expects all its elements to be compatible with Child.

Highlight answered 2/9, 2009 at 14:54 Comment(7)
+1. That is really the practical difference. extends to fetch, super to insert.Deibel
@Jon what do you mean by (In both cases E itself is okay.) in the first paragraph ?Servomechanical
@Geek: I mean that if you have something like ? extends InputStream or ? super InputStream then you can use an InputStream as the argument.Highlight
I never really got the PECS explanation by Josh Block in effective Java. However @Yishai, this is a helpful way to remember. Perhaps we can propose a new mnemonic, SAGE: Super -> Add / Get -> ExtendBrazier
So if I read it right, "<? extends E>" requires that "?" is a subclass of "E", and "<? super E>" requires that "E" is a subclass of "?", right?Derk
"The first (<? super E>) says that it's "some type which is an ancestor (superclass) of E"" But a list<? super E> can only hold elements E or subtypes, so could this decription be wrong?Sievers
@Rubus: No, your assertion is untrue. See gist.github.com/jskeet/5b51e8d90d21c343971382e4dde31ee3 for example, which shows a List<? super Subclass> contains an element Superclass, which exactly matches my description.Highlight
R
173

The reasons for this are based on how Java implements generics.

An Arrays Example

With arrays you can do this (arrays are covariant)

Integer[] myInts = {1,2,3,4};
Number[] myNumber = myInts;

But, what would happen if you try to do this?

myNumber[0] = 3.14; //attempt of heap pollution

This last line would compile just fine, but if you run this code, you could get an ArrayStoreException. Because you’re trying to put a double into an integer array (regardless of being accessed through a number reference).

This means that you can fool the compiler, but you cannot fool the runtime type system. And this is so because arrays are what we call reifiable types. This means that at runtime Java knows that this array was actually instantiated as an array of integers which simply happens to be accessed through a reference of type Number[].

So, as you can see, one thing is the actual type of the object, and another thing is the type of the reference that you use to access it, right?

The Problem with Java Generics

Now, the problem with Java generic types is that the type information is discarded by the compiler and it is not available at run time. This process is called type erasure. There are good reason for implementing generics like this in Java, but that's a long story, and it has to do, among other things, with binary compatibility with pre-existing code (see How we got the generics we have).

But the important point here is that since, at runtime there is no type information, there is no way to ensure that we are not committing heap pollution.

For instance,

List<Integer> myInts = new ArrayList<Integer>();
myInts.add(1);
myInts.add(2);

List<Number> myNums = myInts; //compiler error
myNums.add(3.14); //heap pollution

If the Java compiler does not stop you from doing this, the runtime type system cannot stop you either, because there is no way, at runtime, to determine that this list was supposed to be a list of integers only. The Java runtime would let you put whatever you want into this list, when it should only contain integers, because when it was created, it was declared as a list of integers.

As such, the designers of Java made sure that you cannot fool the compiler. If you cannot fool the compiler (as we can do with arrays) you cannot fool the runtime type system either.

As such, we say that generic types are non-reifiable.

Evidently, this would hamper polymorphism. Consider the following example:

static long sum(Number[] numbers) {
   long summation = 0;
   for(Number number : numbers) {
      summation += number.longValue();
   }
   return summation;
}

Now you could use it like this:

Integer[] myInts = {1,2,3,4,5};
Long[] myLongs = {1L, 2L, 3L, 4L, 5L};
Double[] myDoubles = {1.0, 2.0, 3.0, 4.0, 5.0};

System.out.println(sum(myInts));
System.out.println(sum(myLongs));
System.out.println(sum(myDoubles));

But if you attempt to implement the same code with generic collections, you will not succeed:

static long sum(List<Number> numbers) {
   long summation = 0;
   for(Number number : numbers) {
      summation += number.longValue();
   }
   return summation;
}

You would get compiler erros if you try to...

List<Integer> myInts = asList(1,2,3,4,5);
List<Long> myLongs = asList(1L, 2L, 3L, 4L, 5L);
List<Double> myDoubles = asList(1.0, 2.0, 3.0, 4.0, 5.0);

System.out.println(sum(myInts)); //compiler error
System.out.println(sum(myLongs)); //compiler error
System.out.println(sum(myDoubles)); //compiler error

The solution is to learn to use two powerful features of Java generics known as covariance and contravariance.

Covariance

With covariance you can read items from a structure, but you cannot write anything into it. All these are valid declarations.

List<? extends Number> myNums = new ArrayList<Integer>();
List<? extends Number> myNums = new ArrayList<Float>();
List<? extends Number> myNums = new ArrayList<Double>();

And you can read from myNums:

Number n = myNums.get(0); 

Because you can be sure that whatever the actual list contains, it can be upcasted to a Number (after all anything that extends Number is a Number, right?)

However, you are not allowed to put anything into a covariant structure.

myNumst.add(45L); //compiler error

This would not be allowed, because Java cannot guarantee what is the actual type of the object in the generic structure. It can be anything that extends Number, but the compiler cannot be sure. So you can read, but not write.

Contravariance

With contravariance you can do the opposite. You can put things into a generic structure, but you cannot read out from it.

List<Object> myObjs = new List<Object>();
myObjs.add("Luke");
myObjs.add("Obi-wan");

List<? super Number> myNums = myObjs;
myNums.add(10);
myNums.add(3.14);

In this case, the actual nature of the object is a List of Objects, and through contravariance, you can put Numbers into it, basically because all numbers have Object as their common ancestor. As such, all Numbers are objects, and therefore this is valid.

However, you cannot safely read anything from this contravariant structure assuming that you will get a number.

Number myNum = myNums.get(0); //compiler-error

As you can see, if the compiler allowed you to write this line, you would get a ClassCastException at runtime (because item 0 in the list is an Object, not a Number).

Get/Put Principle

As such, use covariance when you only intend to take generic values out of a structure, use contravariance when you only intend to put generic values into a structure, and use the exact generic type when you intend to do both.

The best example I have is the following which copies any kind of numbers from one list into another list. It only gets items from the source, and it only puts items in the target.

public static void copy(List<? extends Number> source, List<? super Number> target) {
    for(Number number : source) {
        target.add(number);
    }
}

Thanks to the powers of covariance and contravariance this works for a case like this:

List<Integer> myInts = asList(1,2,3,4);
List<Double> myDoubles = asList(3.14, 6.28);
List<Object> myObjs = new ArrayList<Object>();

copy(myInts, myObjs);
copy(myDoubles, myObjs);
Rosebay answered 16/11, 2012 at 2:56 Comment(4)
This answer should bump to top. Nice explanation.Stir
@edwindalorzo, there's a small typo you want to fix under Contravariance. You say List<Object> myObjs = new List<Object(); (which is missing the closing > for the second Object).Epistyle
Fantastic, easy, and clear examples of these subtle concepts!Ionization
Something you could add to help others remember things. When you want to call a method from a super class, you use super.methodName. When using <? super E>, it means "something in the super direction" as opposed to something in the extends direction. Example: Object is in the super direction of Number (since it is a super class) and Integer is in the extends direction (since it extends Number).Betony
Q
75

<? extends E> defines E as the upper bound: "This can be cast to E".

<? super E> defines E as the lower bound: "E can be cast to this."

Quyenr answered 2/9, 2009 at 14:57 Comment(8)
This is one of the best simple/practical summaries of the difference that I've seen.Deedee
For decades now (with OOP) I've been fighting an instinctive inversion of the notions "upper" and "lower". Aggravating! To me, Object is inherently a lower level class, despite it's position as the ultimate superclass (and vertically drawn in UML or similar inheritance trees). I've never been able to undo this despite eons of trying.Epistyle
@tgm1024 "superclass" and "subclass" must give you a lot of trouble.Quyenr
@DavidMoles, why? You're clearly not following what I'm saying at all. "superclass" is akin to "superset"; the notion of specialization is a reduction in applicability under the IS-A relationship. Apple IS-A Fruit. Fruit (superclass) is a superset including Apple (subclass) as a subset. That verbal relationship is fine. What I'm saying breaks down is the notion that "upper" and "lower" have intrinsic mappings to "superset" and "subset". Upper and Lower should be avoided as terms in OO.Epistyle
@tgm1024 "Super-" comes from the Latin super "above, on", and "sub-" from the Latin sub "under, beneath". That is to say, etymologically, super is up and sub is down.Quyenr
@DavidMoles, and yet a sub class can provide additional functionality that a super class cannot. Look, this is absurd. The bottom line is that your assertion that I must have trouble with subclass and superclass is without merit. I don't.Epistyle
Perfect explanation...no jargon, just the key differences.Ribosome
@DavidMoles I had a similar awakening, if i may, to the concept of super and sub classes while working on a problem of up/down casting. Upcasting to parent class leads to method hiding of the original class and as such while upcasting a class till the Object class in hierarchy chain, they loose all their behavior / methods. After this, the term super seems to loose all its appeal and come down to bare minimum.Toughie
P
17

<? super E> means any object including E that is parent of E

<? extends E> means any object including E that is child of E .

Panda answered 16/5, 2015 at 13:6 Comment(2)
short and sweet answer.Ruthie
Based on your first "explanation";List<? super Parent> list = new ArrayList<>(); list.add(new Child()); (where Child is subclass of Parent ) this must be invalid but this compiles and is validSemiaquatic
S
14

I'm going to try and answer this. But to get a really good answer you should check Joshua Bloch's book Effective Java (2nd Edition). He describes the mnemonic PECS, which stands for "Producer Extends, Consumer Super".

The idea is that if you code is consuming the generic values from the object then you should use extends. but if you are producing new values for the generic type you should use super.

So for example:

public void pushAll(Iterable<? extends E> src) {
  for (E e: src) 
    push(e);
}

And

public void popAll(Collection<? super E> dst) {
  while (!isEmpty())
    dst.add(pop())
}

But really you should check out this book: http://java.sun.com/docs/books/effective/

Spiffy answered 2/9, 2009 at 15:0 Comment(0)
A
8

You might want to google for the terms contravariance (<? super E>) and covariance (<? extends E>). I found that the most useful thing when comprehending generics was for me to understand the method signature of Collection.addAll:

public interface Collection<T> {
    public boolean addAll(Collection<? extends T> c);
}

Just as you'd want to be able to add a String to a List<Object>:

List<Object> lo = ...
lo.add("Hello")

You should also be able to add a List<String> (or any collection of Strings) via the addAll method:

List<String> ls = ...
lo.addAll(ls)

However you should realize that a List<Object> and a List<String> are not equivalent and nor is the latter a subclass of the former. What is needed is the concept of a covariant type parameter - i.e. the <? extends T> bit.

Once you have this, it's simple to think of scenarios where you want contravariance also (check the Comparable interface).

Arsenite answered 2/9, 2009 at 14:55 Comment(0)
N
6

Before the answer; Please be clear that

  1. Generics only compile time feature to ensure TYPE_SAFETY, it wont b available during RUNTIME.
  2. Only a reference with Generics will force the type safety; if the reference don't declared with generics then it will work without type safty.

Example:

List stringList = new ArrayList<String>();
stringList.add(new Integer(10)); // will be successful.

Hope this will help you to understand wildcard more clear.

//NOTE CE - Compilation Error
//      4 - For

class A {}

class B extends A {}

public class Test {

    public static void main(String args[]) {

        A aObj = new A();
        B bObj = new B();
        
        //We can add object of same type (A) or its subType is legal
        List<A> list_A = new ArrayList<A>();
        list_A.add(aObj);
        list_A.add(bObj); // A aObj = new B(); //Valid
        //list_A.add(new String()); Compilation error (CE);
        //can't add other type   A aObj != new String();
         
        
        //We can add object of same type (B) or its subType is legal
        List<B> list_B = new ArrayList<B>();
        //list_B.add(aObj); CE; can't add super type obj to subclass reference
        //Above is wrong similar like B bObj = new A(); which is wrong
        list_B.add(bObj);
        
        

        //Wild card (?) must only come for the reference (left side)
        //Both the below are wrong;   
        //List<? super A> wildCard_Wrongly_Used = new ArrayList<? super A>();
        //List<? extends A> wildCard_Wrongly_Used = new ArrayList<? extends A>();
        
        
        //Both <? extends A>; and <? super A> reference will accept = new ArrayList<A>
        List<? super A> list_4__A_AND_SuperClass_A = new ArrayList<A>();
                        list_4__A_AND_SuperClass_A = new ArrayList<Object>();
                      //list_4_A_AND_SuperClass_A = new ArrayList<B>(); CE B is SubClass of A
                      //list_4_A_AND_SuperClass_A = new ArrayList<String>(); CE String is not super of A  


        List<? extends A> list_4__A_AND_SubClass_A = new ArrayList<A>();
                          list_4__A_AND_SubClass_A = new ArrayList<B>();
                        //list_4__A_AND_SubClass_A = new ArrayList<Object>(); CE Object is SuperClass of A
                          
                          
        //CE; super reference, only accepts list of A or its super classes.
        //List<? super A> list_4__A_AND_SuperClass_A = new ArrayList<String>(); 
                          
        //CE; extends reference, only accepts list of A or its sub classes.
        //List<? extends A> list_4__A_AND_SubClass_A = new ArrayList<Object>();
                          
        //With super keyword we can use the same reference to add objects
        //Any sub class object can be assigned to super class reference (A)                  
        list_4__A_AND_SuperClass_A.add(aObj);
        list_4__A_AND_SuperClass_A.add(bObj); // A aObj = new B();
        //list_4__A_AND_SuperClass_A.add(new Object()); // A aObj != new Object(); 
        //list_4__A_AND_SuperClass_A.add(new String()); CE can't add other type
        
        //We can't put anything into "? extends" structure. 
        //list_4__A_AND_SubClass_A.add(aObj); compilation error
        //list_4__A_AND_SubClass_A.add(bObj); compilation error
        //list_4__A_AND_SubClass_A.add("");   compilation error
    
        //The Reason is below        
        //List<Apple> apples = new ArrayList<Apple>();
        //List<? extends Fruit> fruits = apples;
        //fruits.add(new Strawberry()); THIS IS WORNG :)
    
        //Use the ? extends wildcard if you need to retrieve object from a data structure.
        //Use the ? super wildcard if you need to put objects in a data structure.
        //If you need to do both things, don't use any wildcard.

        
        //Another Solution
        //We need a strong reference(without wild card) to add objects 
        list_A = (ArrayList<A>) list_4__A_AND_SubClass_A;
        list_A.add(aObj);
        list_A.add(bObj);
        
        list_B = (List<B>) list_4__A_AND_SubClass_A;
        //list_B.add(aObj); compilation error
        list_B.add(bObj);

        private Map<Class<? extends Animal>, List<? extends Animal>> animalListMap;

        public void registerAnimal(Class<? extends Animal> animalClass, Animal animalObject) {
    
            if (animalListMap.containsKey(animalClass)) {
                //Append to the existing List
                 /*    The ? extends Animal is a wildcard bounded by the Animal class. So animalListMap.get(animalObject);
                 could return a List<Donkey>, List<Mouse>, List<Pikachu>, assuming Donkey, Mouse, and Pikachu were all sub classes of Animal. 
                 However, with the wildcard, you are telling the compiler that you don't care what the actual type is as long as it is a sub type of Animal.      
                 */   
                //List<? extends Animal> animalList = animalListMap.get(animalObject);
                //animalList.add(animalObject);  //Compilation Error because of List<? extends Animal>
                List<Animal> animalList = animalListMap.get(animalClass);
                animalList.add(animalObject);      


            } 
    }

    }
}
Neurologist answered 6/3, 2013 at 16:4 Comment(3)
@Dana Also See stackoverflow.com/questions/15255929/…Neurologist
My answer on Generic Class type https://mcmap.net/q/15623/-how-to-use-class-lt-t-gt-in-java/…Neurologist
Good explanation with code. But if you had used comments in code outside code block, it would be better for viewing and more readable.Jaye
J
3

A wildcard with an upper bound looks like " ? extends Type " and stands for the family of all types that are subtypes of Type , type Type being included. Type is called the upper bound .

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 .

Joris answered 2/9, 2009 at 14:56 Comment(0)
S
1

You have a Parent class and a Child class inherited from Parent class.The Parent Class is inherited from another class called GrandParent Class.So Order of inheritence is GrandParent > Parent > Child. Now, < ? extends Parent > - This accepts Parent class or either Child class < ? super Parent > - This accepts Parent class or either GrandParent class

Swore answered 19/7, 2018 at 12:58 Comment(1)
I wonder how this answer got upvotes. There is no way to put any element of any kind into a list defined as <? extends Parent>. There are lots of people trying to learn, please don't mislead them for the sake of answerSemiaquatic
M
0

super: List<? super T> 'super' guarantees object to be ADDED to the collection is of type T.

extends: List<? extends T> 'extend' guarantees object READ from collection is of type T.

Explanation:

There are three things that need to be considered while understanding a difference between 'super' and 'extends' from type safety point of view.

1.Assigning : What type of collection can be assigned to the generic reference.

2.Adding : What type can be added to the collection referred.

3.Reading : What type can be read from collection referred

'<? super T>' ensures-

  1. Any collection of type T or its superclass can be assigned.

  2. Any object of type T or its subclass can be added to collection, as it will always pass a 'Is A' test for T.

  3. Type of the item read from the collection can not be guarantied except from being of type 'Object'. It can be anything of type T or its superclass which includes type 'Object'.

'<? extends T>' ensures-

  1. Any collection of type T or its subclass can be assigned.

  2. No object can be added as we can not determine type of reference. (Even object of type 'T' can not be added, because generic reference might be assigned to the collection of subtype of 'T')

  3. Item read from the collection can be guarantied to be of type 'T'.

Consider class hierarchy

class Base {}

class Intermediate extends Base{}

class ThirdLayer extends Intermediate{}

public void testGenerics() {

    /**
     * super: List<? super T> super guarantees object to be ADDED to the collection
     * if of type T.
     * 
     * extends: List<? extends T> extend guarantees object READ from collection is
     * of type T
     * 
     * Super:- 
     * 
     * Assigning : You can assign collection of Type T or its super classes
     * including 'Object' class.
     * 
     * Adding: You can add objects of anything of Type T or of its subclasses, as we
     * are sure that the object of type T of its subclass always passes Is A test
     * for T. You can NOT add any object of superclass of T.
     * 
     * Reading: Always returns Object
     */

    /**
     * To a Collection of superclass of Intermediate we can assign Collection of
     * element of intermediate or its Parent Class including Object class.
     */
    List<? super Intermediate> lst = new ArrayList<Base>(); 
    lst = new ArrayList<Intermediate>();
    lst = new ArrayList<Object>();

    //Can not assign Collection of subtype
    lst = new ArrayList<ThirdLayer>(); //Error! 

    /** 
     * Elements of subtype of 'Intemediate' can be added as assigned collection is
     * guaranteed to be of type 'Intermediate
     */
    
    lst.add(new ThirdLayer()); 

    lst.add(new Intermediate()); 
    
    // Can not add any object of superclass of Intermediate
    lst.add(new Base()); //Error!

    Object o = lst.get(0);
    
    // Element fetched from collection can not inferred to be of type anything
    // but 'Object'.
    Intermediate thr = lst.get(0); //Error!

    /**
     * extends: List<? extends T> extend guarantees object read from collection is
     * of type T 
     * Assigning : You can assign collection of Type T or its subclasses.
     * 
     * Adding: You cannot add objects of anything of Type T or even objects of its
     * subclasses. This is because we can not be sure about the type of collection
     * assigned to the reference. 
     * 
     * Reading: Always returns object of type 'T'
     */
    
    // Can assign collection of class Intermediate or its subclasses.
    List<? extends Intermediate> lst1 = new ArrayList<ThirdLayer>();
    lst1 = new ArrayList<Base>(); //Error! can not assign super class collection
    
    /**
     *  No element can be added to the collection as we can not be sure of
     *  type of the collection. It can be collection of Class 'Intermediate'
     *  or collection of its subtype. For example if a reference happens to be 
     *  holding a list of class ThirdLayer, it should not be allowed to add an
     *  element of type Intermediate. Hence no addition is allowed including type
     *  'Intermediate'. 
     */
    
    lst1.add(new Base()); //Error!
    lst1.add(new ThirdLayer()); //Error!
    lst1.add(new Intermediate()); //Error!

    
    /**
     * Return type is always guaranteed to be of type 'Intermediate'. Even if the
     * collection hold by the reference is of subtype like 'ThirdLayer', it always
     * passes the 'IS A' test for 'Intermediate'
     */
    Intermediate elm = lst1.get(0); 

    /**
     * If you want a Collection to accept (aka to be allowed to add) elements of
     * type T or its subclasses; simply declare a reference of type T i.e. List<T>
     * myList;
     */

    List<Intermediate> lst3 = new ArrayList<Intermediate>();
    lst3 = new ArrayList<ThirdLayer>(); //Error!
    lst3 = new ArrayList<Base>(); //Error!

    lst3.add(new Intermediate()); 
    lst3.add(new ThirdLayer());  // Allowed as ThirdLayer passes 'IS A' for Intermediate
    lst3.add(new Base()); //Error! No guarantee for superclasses of Intermediate
}
Merrell answered 30/8, 2021 at 13:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.