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

Viewed 65537

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)
10 Answers

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

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
}
Related