Difference between <? super T> and <? extends T> in Java

What is the difference between List<? super T> List<? super T> and List<? extends T> List<? extends T> ?

I used to use List<? extends T> List<? extends T> , but it does not allow me to add elements to it list.add(e) , whereas the List<? super T> List<? super T> does.


extends

The wildcard declaration of List<? extends Number> foo3 List<? extends Number> foo3 means that any of these are legal assignments:

List<? extends Number> foo3 = new ArrayList<Number>();  // Number "extends" Number (in this context)
List<? extends Number> foo3 = new ArrayList<Integer>(); // Integer extends Number
List<? extends Number> foo3 = new ArrayList<Double>();  // Double extends Number
  • Reading - Given the above possible assignments, what type of object are you guaranteed to read from List foo3 :

  • You can read a Number because any of the lists that could be assigned to foo3 contain a Number or a subclass of Number .
  • You can't read an Integer because foo3 could be pointing at a List<Double> .
  • You can't read a Double because foo3 could be pointing at a List<Integer> .
  • Writing - Given the above possible assignments, what type of object could you add to List foo3 that would be legal for all the above possible ArrayList assignments:

  • You can't add an Integer because foo3 could be pointing at a List<Double> .
  • You can't add a Double because foo3 could be pointing at a List<Integer> .
  • You can't add a Number because foo3 could be pointing at a List<Integer> .
  • You can't add any object to List<? extends T> List<? extends T> because you can't guarantee what kind of List it is really pointing to, so you can't guarantee that the object is allowed in that List . The only "guarantee" is that you can only read from it and you'll get a T or subclass of T .

    super

    Now consider List <? super T> List <? super T> .

    The wildcard declaration of List<? super Integer> foo3 List<? super Integer> foo3 means that any of these are legal assignments:

    List<? super Integer> foo3 = new ArrayList<Integer>();  // Integer is a "superclass" of Integer (in this context)
    List<? super Integer> foo3 = new ArrayList<Number>();   // Number is a superclass of Integer
    List<? super Integer> foo3 = new ArrayList<Object>();   // Object is a superclass of Integer
    
  • Reading - Given the above possible assignments, what type of object are you guaranteed to receive when you read from List foo3 :

  • You aren't guaranteed an Integer because foo3 could be pointing at a List<Number> or List<Object> .
  • You aren't guaranteed a Number because foo3 could be pointing at a List<Object> .
  • The only guarantee is that you will get an instance of an Object or subclass of Object (but you don't know what subclass).
  • Writing - Given the above possible assignments, what type of object could you add to List foo3 that would be legal for all the above possible ArrayList assignments:

  • You can add an Integer because an Integer is allowed in any of above lists.
  • You can add an instance of a subclass of Integer because an instance of a subclass of Integer is allowed in any of the above lists.
  • You can't add a Double because foo3 could be pointing at an ArrayList<Integer> .
  • You can't add a Number because foo3 could be pointing at an ArrayList<Integer> .
  • You can't add an Object because foo3 could be pointing at an ArrayList<Integer> .
  • PECS

    Remember PECS: "Producer Extends, Consumer Super" .

  • "Producer Extends" - If you need a List to produce T values (you want to read T s from the list), you need to declare it with ? extends T ? extends T , eg List<? extends Integer> List<? extends Integer> . But you cannot add to this list.

  • "Consumer Super" - If you need a List to consume T values (you want to write T s into the list), you need to declare it with ? super T ? super T , eg List<? super Integer> List<? super Integer> . But there are no guarantees what type of object you may read from this list.

  • If you need to both read from and write to a list, you need to declare it exactly with no wildcards, eg List<Integer> .

  • Example

    Note this example from the Java Generics FAQ. Note how the source list src (the producing list) uses extends , and the destination list dest (the consuming list) uses super :

    public class Collections { 
      public static <T> void copy(List<? super T> dest, List<? extends T> src) {
          for (int i = 0; i < src.size(); i++) 
            dest.set(i, src.get(i)); 
      } 
    }
    

    Also see How can I add to List<? extends Number> data structures?


    Imagine having this hierarchy

    在这里输入图像描述

    1. Extends

    By writing

        List<? extends C2> list;
    

    you are saying that list will be able to reference an object of type (for example) ArrayList whose generic type is one of the 7 subtypes of C2 ( C2 included):

  • C2: new ArrayList<C2>(); , (an object that can store C2 or subtypes) or
  • D1: new ArrayList<D1>(); , (an object that can store D1 or subtypes) or
  • D2: new ArrayList<D2>(); , (an object that can store D2 or subtypes) or...
  • and so on. Seven different cases:

        1) new ArrayList<C2>(): can store C2 D1 D2 E1 E2 E3 E4
        2) new ArrayList<D1>(): can store    D1    E1 E2  
        3) new ArrayList<D2>(): can store       D2       E3 E4
        4) new ArrayList<E1>(): can store          E1             
        5) new ArrayList<E2>(): can store             E2             
        6) new ArrayList<E3>(): can store                E3             
        7) new ArrayList<E4>(): can store                   E4             
    

    We have a set of "storable" types for each possible case: 7 (red) sets here graphically represented

    在这里输入图像描述

    As you can see, there is not a safe type that is common to every case:

  • you cannot list.add(new C2(){}); because it could be list = new ArrayList<D1>();
  • you cannot list.add(new D1(){}); because it could be list = new ArrayList<D2>();
  • and so on.

    2. Super

    By writing

        List<? super C2> list;
    

    you are saying that list will be able to reference an object of type (for example) ArrayList whose generic type is one of the 7 supertypes of C2 ( C2 included):

  • A1: new ArrayList<A1>(); , (an object that can store A1 or subtypes) or
  • A2: new ArrayList<A2>(); , (an object that can store A2 or subtypes) or
  • A3: new ArrayList<A3>(); , (an object that can store A3 or subtypes) or...
  • and so on. Seven different cases:

        1) new ArrayList<A1>(): can store A1          B1 B2       C1 C2    D1 D2 E1 E2 E3 E4
        2) new ArrayList<A2>(): can store    A2          B2       C1 C2    D1 D2 E1 E2 E3 E4
        3) new ArrayList<A3>(): can store       A3          B3       C2 C3 D1 D2 E1 E2 E3 E4
        4) new ArrayList<A4>(): can store          A4       B3 B4    C2 C3 D1 D2 E1 E2 E3 E4
        5) new ArrayList<B2>(): can store                B2       C1 C2    D1 D2 E1 E2 E3 E4
        6) new ArrayList<B3>(): can store                   B3       C2 C3 D1 D2 E1 E2 E3 E4
        7) new ArrayList<C2>(): can store                            C2    D1 D2 E1 E2 E3 E4
    

    We have a set of "storable" types for each possible case: 7 (red) sets here graphically represented

    在这里输入图像描述

    As you can see, here we have seven safe types that are common to every case: C2 , D1 , D2 , E1 , E2 , E3 , E4 .

  • you can list.add(new C2(){}); because, regardless of the kind of List we're referencing, C2 is allowed
  • you can list.add(new D1(){}); because, regardless of the kind of List we're referencing, D1 is allowed
  • and so on. You probably noticed that these types correspond to the hierarchy starting from type C2 .

    Notes

    Here the complete hierarchy if you wish to make some tests

    interface A1{}
    interface A2{}
    interface A3{}
    interface A4{}
    
    interface B1 extends A1{}
    interface B2 extends A1,A2{}
    interface B3 extends A3,A4{}
    interface B4 extends A4{}
    
    interface C1 extends B2{}
    interface C2 extends B2,B3{}
    interface C3 extends B3{}
    
    interface D1 extends C1,C2{}
    interface D2 extends C2{}
    
    interface E1 extends D1{}
    interface E2 extends D1{}
    interface E3 extends D2{}
    interface E4 extends D2{}
    

    I love the answer from @Bert F but this is the way my brain sees it.

    I have an X in my hand. If I want to write my X into a List, that List needs to be either a List of X or a List of things that my X can be upcast to as I write them in ie any superclass of X...

    List<? super   X>
    

    If I get a List and I want to read an X out of that List, that better be a List of X or a List of things that can be upcast to X as I read them out, ie anything that extends X

    List<? extends X>
    

    Hope this helps.

    链接地址: http://www.djcxy.com/p/91976.html

    上一篇: Java:私有内部类合成构造函数

    下一篇: <?之间的区别 超级T>和<? 在Java中扩展T>