Types in Hamcrest Matchers

29 views
Skip to first unread message

Joe Kearney

unread,
Sep 9, 2008, 6:01:07 AM9/9/08
to Hamcrest Developers
Hi all,

I'd like to start a discussion on the use of generic types throughout
Hamcrest, starting on two particular issues:
* We still can't cater for subtypes properly (issues 31 and 32
touch upon this)
* Matchers matching a single part of a map entry (key or value)
fail to work when the map is raw or parametrised (I'll enter a bug
report for this shortly)

There are a number of assertions which seem reasonable to want to make
using matchers, not all of which are currently allowed the compiler
due to generic issues. Some examples:
Suppose we have types T and SubT extends T, and objects t, st of
those respective types.
1. assertThat(t, matches(t)) // e.g. assertThat(3, lessThan(4))
compiles
2. assertThat(st, matches(t)) // e.g. assertThat(3.0,
lessThan((Number)4)) compiles in trunk
3. assertThat(t, matches(st)) // e.g. assertThat((Number)3,
lessThan(4.0)) fails
It is arguable that we should also expect to be able to compile
assertThat(3, lessThan("Hello")), though obviously expect it to fail.
Relate this to the reason why Set<T>#contains() takes an Object rather
than a T.

The current signature (in trunk, though not in the download) of
assertThat() is
<T> void assertThat(T, Matcher<? super T>)
and allows assertions 1 and 2. The previous version (without the
wildcard) allows only assertion 1, and (I believe) the same with an
upper bound wildcard allows assertions 1 and 3.

We can't cater for all three cases if we wish to enforce a subtype
relation (in either direction) between the value type and the Matcher
parameter type. Thus the choices are
* Disassociate the parameter type from the value type, as follows,
though this would seem to be superfluous - what does S mean?
<T, S> void assertThat(T, Matcher<S>)
* Remove the parameter from Matcher altogether. This has serious
issues for backwards compaibility, especially since JUnit has a copy
of the assertThat methods.

Obviously the latter will require a lot of changes, though it will
also fix a number of issues like the following compile error (in the
release, I haven't checked trunk recently):
assertThat(new HashMap<X,Y>(), hasKey(new X()))
assertThat(new HashMap(), hasKey(new X()))

Both of these fail to compile. These can both be fixed independently
of the above proposal, by changing a lot of signatures in Matchers and
IsMapContaining to return Matcher<? super Map<X, ?>> (to match the
key, vice versa to match the value).


I'm interested in people's opinions over what should be done here.
* My proposal is to remove the parameter type from Matcher,
BaseMatcher and all subclasses except for (some) subtypes of
TypeSafeMatcher, which perhaps still has a place.
* Perhaps something clever can be done with type names and adding
types to different places in the hierarchy to reduce the backwards
compatibility issues that this will create.
* For the map matcher issues above, some of the inferred types K
and V in all of the @Factory methods seem superfluous - if we're
matching a key of type K, we know nothing about type V so it should be
a wildcard.

Many thanks,
Joe

Nat Pryce

unread,
Sep 14, 2008, 2:08:20 PM9/14/08
to hamcre...@googlegroups.com
2008/9/9 Joe Kearney <joe.k...@morganstanley.com>:

> I'm interested in people's opinions over what should be done here.
> * My proposal is to remove the parameter type from Matcher,
> BaseMatcher and all subclasses except for (some) subtypes of
> TypeSafeMatcher, which perhaps still has a place.

That will break jMock.

JMock uses the static type parameter to get compile-time knowledge of
the type of a method's actual parameter, so that when it captures an
expectation it can stash the matcher and return a fake value of the
appropriate type.

The Matcher accepts an Object to match because jMock does not know the
value to be matched at run time, and may use the same matcher to match
incoming invocations for different methods with parameters of
different types.

--Nat

Reply all
Reply to author
Forward
0 new messages