GhostCat
GhostCat

Reputation: 140427

How to make JUnit assertThat() work with lower bounded wildcard?

initially, I had this piece of production code:

interface ActionSequence {
   public List<Actions> getActions();

I tested classes implementing that interface with something like this:

assertThat(sequenceUnderTest.getActions(), is(Arrays.asList(action1, action2));

Then I figured that it might be beneficial to change the production interface to:

public List<? extends Action> getActions() {

(to allow me to return lists of subclasses of Action).

But now eclipse tells me:

The method assertThat(T, Matcher<? super T>) in the type Assert is not applicable for the arguments (List<capture#1-of ? extends Action>, Matcher<List<Action>>)

I figured: when I change the class that implements ActionInterface to do

@Override
public List<SomeSubClassOfAction> getActions()

(instead of keeping the wildcard) ... then everything works. But why?

Upvotes: 1

Views: 513

Answers (2)

Frank Neblung
Frank Neblung

Reputation: 3175

Your question was, why

@Override
public List<SomeSubClassOfAction> getActions()

is a legal Implementation of

public List<? extends Action> getActions()

The Answer is covariant return. Since Java1.5 subclasses are allowed to specialize the return of inherited methods.

But I wouldn't recommend having wildcard types in return parameters because it isn't client friendly. See Generic wildcard types should not be used in return parameters and its quote from Effective Java

Upvotes: 1

stefan.m
stefan.m

Reputation: 2012

Arrays.asList(action1, action2) will return a List<Action>. is(Arrays.asList(action1, action2)) will therefore return a Matcher<List<Action>>.

assertThat has the following signature:

assertThat(T actual, Matcher<T> matcher)

So assertThat requires the following parameters in your case:

assertThat(List<Action>, Matcher<List<Action>>)

But your first parameter is a List<? extends Action> And a List<Action> is completely different from a List<? extends Action>. For example, you cannot put Action elements into a List<SomeSubClassOfAction>. This is why this won't work.

For details, see Angelika Langer's excellent site: http://www.angelikalanger.com/GenericsFAQ/FAQSections/Index.html

Upvotes: 2

Related Questions