npryce/make-it-easy

listOf() does not compile for one item of a subtype for a supertype collection

GoogleCodeExporter opened this issue · 2 comments

What steps will reproduce the problem?
1. Change constructor of FruitBowl to only take a list of Fruit instead of a 
bounded wildcard, i.e.:
    public FruitBowl(List<Fruit> contents) {
        ...
    }
2. Change FruitBowlMaker accordingly to solve compilation errors, i.e.:
public class FruitBowlMaker {
    public static final Property<FruitBowl, List<Fruit>> contents = 
        newProperty();

    public static final Instantiator<FruitBowl> FruitBowl = 
        new Instantiator<FruitBowl>() {

    public FruitBowl instantiate(PropertyLookup<FruitBowl> lookup) {
        return new FruitBowl(lookup.valueOf(contents, listOf(
            an(Apple), a(Banana)).value()));
        }
    };
}

3. Remove the banana, leaving only a single item in the list. Alternatively, 
replace it with another apple, leaving two items but of the same type in the 
list.

What is the expected output? What do you see instead?
It should compile, since an apple is a fruit, and so are two, but because of 
the way Java implements Generics, listOf() now returns a List<Apple> which is 
not a List<Fruit>. The exact compiler error is:
The method valueOf(Property<? super FruitBowl,V>, V) in the type 
PropertyLookup<FruitBowl> is not applicable for the arguments 
(Property<FruitBowl,List<Fruit>>, List<Apple>)

What version of the product are you using? On what operating system?
3.1.0 on Ubuntu 10.10 with OpenJDK 6

Please provide any additional information below.
I know this is probably a limitation of Java but the example gives the illusion 
that it will work for any combination of fruit. I was also wondering what 
possible workarounds there are

Thanks
Thomas

Original issue reported on code.google.com by holz.tho...@gmail.com on 21 Jan 2012 at 11:54

The never-ending wonders of Java Generics ...
Turns out that you can explicitly type the return type of the lastOf() function 
to return a list of a super type of the one passed in, like so:

    public static void howToMakeCollectionsWithValuesOfASingleSubtype() {
        FruitBowl bowl = make(a(FruitBowl, with(contents,
                MakeItEasy.<Fruit>listOf(a(Banana))
        )));
    }

The only caveat, as far as I know, is that the function needs to be qualified 
with the class name in front even with a static import. 


Original comment by holz.tho...@gmail.com on 22 Jan 2012 at 1:15

It works if you don't remove the bounded wildcard in the constructor. This isn't something that can be fixed in the listOf function.