[ACCEPTED]-How can elements be added to a wildcard generic collection?-generics

Accepted answer
Score: 33

Use this instead:

1  public List<? extends Foo> getFoos()
2  {
3    List<Foo> foos = new ArrayList<Foo>(); /* Or List<SubFoo> */
4    foos.add(new SubFoo());
5    return foos;
6  }

Once you declare foos as 4 List<? extends Foo>, the compiler doesn't know that it's safe 3 to add a SubFoo. What if an ArrayList<AltFoo> had been assigned 2 to foos? That would be a valid assignment, but 1 adding a SubFoo would pollute the collection.

Score: 20

Just thought I'd add to this old thread, by 5 summarising the properties of List parameters 4 instantiated with types or wildcards....

When 3 a method has a parameter/result which is 2 a List, the use of type instantiation or 1 wildcards determines

  1. Types of List which can be passed to the method as an argument
  2. Types of List which can be populated from the method result
  3. Types of elements which can be written to list within the method
  4. Types which can be populated when reading elements from list within the method

Param/Return type: List< Foo>

  1. Types of List which can be passed to the method as an argument:
    • List< Foo>
  2. Types of List which can be populated from the method result:
    • List< Foo>
    • List< ? super Foo>
    • List< ? super SubFoo>
    • List< ? extends Foo>
    • List< ? extends SuperFoo>
  3. Types of elements which can be written to list within the method:
    • Foo & subtypes
  4. Types which can be populated when reading elements from list within the method:
    • Foo & supertypes (up to Object)

Param/Return type: List< ? extends Foo>

  1. Types of List which can be passed to the method as an argument:
    • List< Foo>
    • List< Subfoo>
    • List< SubSubFoo>
    • List< ? extends Foo>
    • List< ? extends SubFoo>
    • List< ? extends SubSubFoo>
  2. Types of List which can be populated from the method result:
    • List< ? extends Foo>
    • List< ? extends SuperFoo>
    • List< ? extends SuperSuperFoo>
  3. Types of elements which can be written to list within the method:
    • None! Not possible to add.
  4. Types which can be populated when reading elements from list within the method:
    • Foo & supertypes (up to Object)

Param/Return type: List<? super Foo>

  1. Types of List which can be passed to the method as an argument:
    • List< Foo>
    • List< Superfoo>
    • List< SuperSuperFoo>
    • List< ? super Foo>
    • List< ? super SuperFoo>
    • List< ? super SuperSuperFoo>
  2. Types of List which can be populated from the method result:
    • List< ? super Foo>
    • List< ? super SubFoo>
    • List< ? super SubSubFoo>
  3. Types of elements which can be written to list within the method:
    • Foo & supertypes
  4. Types which can be populated when reading elements from list within the method:
    • Foo & supertypes (up to Object)

Interpretation/Comment

  • needs of external callers drive the design of the method declaration i.e. the public API (normally the primary consideration)
  • needs of internal method logic drive any additional decisions re actual data types declared and constructed internally (normally the secondary consideration)
  • use List<Foo> if caller code is always focused on manipulating the Foo class, as it maximises flexibility for both read and write
  • use List<? extends UpperMostFoo> if there could be many different types of caller, focused on manipulating a different class (not always Foo) and there is a single uppermost class in the Foo type hierarchy, and if the method is to internally write to the list and caller list manipulation is reading. Here the method may internally use List< UpperMostFoo> and add elements to it, before returning List< ? extends UpperMostFoo>
  • if there could be many different types of caller, focused on manipulating a different class (not always Foo) and if reading and writing to list is required and there is a single lowest class in the Foo type hierarchy, then it makes sense to use List< ? super LowerMostFoo>
Score: 7

Try:

public List<Foo> getFoos() {
    List<Foo> foos = new ArrayList<Foo>();
    foos.add(new SubFoo());
    return foos;
}

The generic ArrayList constructor needs 24 to have a specific type to be parameterized 23 on, you cannot use the '?' wildcard there. Changing 22 the instantiation to "new ArrayList<Foo>()' would 21 solve the first compilation error.

The declaration 20 of the 'foos' variable can have wildcards, but 19 since you know the precise type, it makes 18 more sense to reference the same type info 17 there. What you have now says that foos 16 holds some specific subtype of Foo, but 15 we don't know which. Adding a SubFoo may 14 not be allowed, since a SubFoo is not "all 13 subtypes of Foo". Changing the declaration 12 to 'List<Foo> foos = ' solves the 11 second compilation error.

Finally, I would 10 change the return type to 'List<Foo>' since 9 clients of this method won't be able to 8 do much with the returned value as currently 7 defined. You should rarely use wildcards 6 in return types. Use a parameterized method 5 signature if needed, but prefer bounded 4 types to only appear in method arguments, as 3 that leaves it up to the caller who can 2 pass in specific types and operate and them 1 accordingly.

Score: 3

To get an idea of how generics works check 4 out this example:

    List<SubFoo> sfoo = new ArrayList<SubFoo>();
    List<Foo> foo;
    List<? extends Foo> tmp;

    tmp = sfoo;
    foo = (List<Foo>) tmp;

The thing is, that wasn't 3 designed for local/member variables, but 2 for function signatures, that's why it's 1 so ass-backwards.

Score: 2

The following will work fine:

public List<? extends Foo> getFoos() {
    List<Foo> foos = new ArrayList<Foo>();
    foos.add(new SubFoo());
    return foos;
}

0

More Related questions