How can elements be added to a wildcard generic collection?

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 List<? extends Foo>, the compiler doesn't know that it's safe to add a SubFoo. What if an ArrayList<AltFoo> had been assigned to foos? That would be a valid assignment, but adding a SubFoo would pollute the collection.


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

When a method has a parameter/result which is a List, the use of type instantiation or 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>

Try:

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

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

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

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


To get an idea of how generics works check 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 designed for local/member variables, but for function signatures, that's why it's so ass-backwards.

Tags:

Java

Generics