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

前端 未结 6 979
暗喜
暗喜 2020-11-22 02:19

I have a List which is declared like this :

 List foo3 = new ArrayList();

I tried to add 3 to foo3.

6条回答
  •  别那么骄傲
    2020-11-22 02:56

    Sorry, but you can't.

    The wildcard declaration of List foo3 means that the variable foo3 can hold any value from a family of types (rather than any value of a specific type). It means that any of these are legal assignments:

    List foo3 = new ArrayList;  // Number "extends" Number
    List foo3 = new ArrayList; // Integer extends Number
    List foo3 = new ArrayList;  // Double extends Number
    

    So, given this, what type of object could you add to List foo3 that would be legal after any of the above possible ArrayList assignments:

    • You can't add an Integer because foo3 could be pointing at a List.
    • You can't add a Double because foo3 could be pointing at a List.
    • You can't add a Number because foo3 could be pointing at a List.

    You can't add any object to List 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.

    The reverse logic applies to super, e.g. List. These are legal:

    List foo3 = new ArrayList; // Number is a "super" of Number
    List foo3 = new ArrayList; // Object is a "super" of Number
    
    
    

    You can't read the specific type T (e.g. Number) from List because you can't guarantee what kind of List it is really pointing to. The only "guarantee" you have is you are able to add a value of type T (or any subclass of T) without violating the integrity of the list being pointed to.


    The perfect example of this is the signature for Collections.copy():

    public static  void copy(List dest,List src)
    

    Notice how the src list declaration uses extends to allow me to pass any List from a family of related List types and still guarantee it will produce values of type T or subclasses of T. But you cannot add to the src list.

    The dest list declaration uses super to allow me to pass any List from a family of related List types and still guarantee I can write a value of a specific type T to that list. But it cannot be guaranteed to read the values of specific type T if I read from the list.

    So now, thanks to generics wildcards, I can do any of these calls with that single method:

    // copy(dest, src)
    Collections.copy(new ArrayList(), new ArrayList(), new ArrayList(), new ArrayList());
    Collections.copy(new ArrayList(), new ArrayList
    
    

    Consider this confusing and very wide code to exercise your brain. The commented out lines are illegal and the reason why is stated to the extreme right of the line (need to scroll to see some of them):

      List listNumber_ListNumber  = new ArrayList();
    //List listNumber_ListInteger = new ArrayList();                    // error - can assign only exactly 
    //List listNumber_ListDouble  = new ArrayList();                     // error - can assign only exactly 
    
      List listExtendsNumber_ListNumber  = new ArrayList();
      List listExtendsNumber_ListInteger = new ArrayList();
      List listExtendsNumber_ListDouble  = new ArrayList();
    
      List listSuperNumber_ListNumber  = new ArrayList();
    //List listSuperNumber_ListInteger = new ArrayList();      // error - Integer is not superclass of Number
    //List listSuperNumber_ListDouble  = new ArrayList();       // error - Double is not superclass of Number
    
    
    //List listInteger_ListNumber  = new ArrayList();                  // error - can assign only exactly 
      List listInteger_ListInteger = new ArrayList();
    //List listInteger_ListDouble  = new ArrayList();                  // error - can assign only exactly 
    
    //List listExtendsInteger_ListNumber  = new ArrayList(); // error - Number is not a subclass of Integer
      List listExtendsInteger_ListInteger = new ArrayList();
    //List listExtendsInteger_ListDouble  = new ArrayList(); // error - Double is not a subclass of Integer
    
      List listSuperInteger_ListNumber  = new ArrayList();
      List listSuperInteger_ListInteger = new ArrayList();
    //List listSuperInteger_ListDouble  = new ArrayList();     // error - Double is not a superclass of Integer
    
    
      listNumber_ListNumber.add(3);             // ok - allowed to add Integer to exactly List
    
      // These next 3 are compile errors for the same reason:
      // You don't know what kind of List is really
      // being referenced - it may not be able to hold an Integer.
      // You can't add anything (not Object, Number, Integer,
      // nor Double) to List      
    //listExtendsNumber_ListNumber.add(3);     // error - can't add Integer to *possible* List, even though it is really List
    //listExtendsNumber_ListInteger.add(3);    // error - can't add Integer to *possible* List, even though it is really List
    //listExtendsNumber_ListDouble.add(3);     // error - can't add Integer to *possible* List, especially since it is really List
    
      listSuperNumber_ListNumber.add(3);       // ok - allowed to add Integer to List or List
    
      listInteger_ListInteger.add(3);          // ok - allowed to add Integer to exactly List (duh)
    
      // This fails for same reason above - you can't
      // guarantee what kind of List the var is really
      // pointing to
    //listExtendsInteger_ListInteger.add(3);   // error - can't add Integer to *possible* List that is only allowed to hold X's
    
      listSuperInteger_ListNumber.add(3);      // ok - allowed to add Integer to List, List, or List
      listSuperInteger_ListInteger.add(3);     // ok - allowed to add Integer to List, List, or List
    
        

    提交回复
    热议问题