Joe Kearney
unread,Sep 9, 2008, 6:01:07 AM9/9/08Sign in to reply to author
Sign in to forward
You do not have permission to delete messages in this group
Either email addresses are anonymous for this group or you need the view member email addresses permission to view the original message
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