Why do I get compiler errors with this Java code?
1 public List extends Foo> getFoos()
2 {
3 List extends Foo> foos = new ArrayList
The following will work fine:
public List<? extends Foo> getFoos() {
List<Foo> foos = new ArrayList<Foo>();
foos.add(new SubFoo());
return foos;
}
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.
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
List< Foo>
List< Foo>
List< Foo>
List< ? super Foo>
List< ? super SubFoo>
List< ? extends Foo>
List< ? extends SuperFoo>
Foo
& subtypesFoo
& supertypes (up to Object
)List< ? extends Foo>
List< Foo>
List< Subfoo>
List< SubSubFoo>
List< ? extends Foo>
List< ? extends SubFoo>
List< ? extends SubSubFoo>
List< ? extends Foo>
List< ? extends SuperFoo>
List< ? extends SuperSuperFoo>
Foo
& supertypes (up to Object
)List<? super Foo>
List< Foo>
List< Superfoo>
List< SuperSuperFoo>
List< ? super Foo>
List< ? super SuperFoo>
List< ? super SuperSuperFoo>
List< ? super Foo>
List< ? super SubFoo>
List< ? super SubSubFoo>
Foo
& supertypesFoo
& supertypes (up to Object
)List<Foo>
if caller code is always focused on manipulating the Foo class, as it maximises flexibility for both read and writeList<? 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>
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.
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.