0
votes

I don't understand why this code with Java generics fails to compile. Given this class:

public static class X {
    public List<? extends X> getList() {
        return null;
    }
}

there's no way to mock the getList return value:

X x = mock(X.class);
// Error: Cannot resolve method 'thenReturn(List<T>)'
when(x.getList()).thenReturn(Collections.singletonList(new X()));
// Error: Cannot resolve method 'thenReturn(List<capture of ? extends X>)'
when(x.getList()).thenReturn((List<? extends X>) Collections.singletonList(new X()));
// Error: Cannot resolve method 'thenReturn(List<capture of ?>)'
when(x.getList()).thenReturn((List<?>) Collections.singletonList(new X()));

The only way of making it work with "when...then" seems to be stripping generics entirely, at the cost of a compiler warning:

when(x.getList()).thenReturn((List) Collections.singletonList(new X()));

Ultimately, only this works:

doReturn(Collections.singletonList(new X())).when(x.getList());
1
checked here? - Pp88

1 Answers

0
votes

Try this:

when(x.getList()).thenReturn(Collections.<? extends X>singletonList(new X()));
//                                       ^^^^^^^^^^^^^

The generic method <T> singletonList(T o) is going to infer List<X> instead of List<? extends X>, and the former is not assignable to a parameter of the latter. If they were assignable to each other, you could have the same list instance apply to List<X> xList and List<SpecificSubtypeOfX> subtypeList, add a SomeOtherSubtypeOfX instance to xList, and then it would be completely unexpected in subtypeList.

The explicit type parameter (called a "type witness" in the documentation) tells Java that its inference needs to be more generous than it would be otherwise, which would make your collection match the method.

This doesn't have anything to do with Mockito other than that your doReturn method works because Mockito can't check return types in doReturn syntax.