Given the following example (using JUnit with Hamcrest matchers):
Map<String, Class<? extends Serializable>> expected = null;
Map<String, Class<java.util.Date>> result = null;
assertThat(result, is(expected));
This does not compile with the JUnit assertThat
method signature of:
public static <T> void assertThat(T actual, Matcher<T> matcher)
The compiler error message is:
Error:Error:line (102)cannot find symbol method
assertThat(java.util.Map<java.lang.String,java.lang.Class<java.util.Date>>,
org.hamcrest.Matcher<java.util.Map<java.lang.String,java.lang.Class
<? extends java.io.Serializable>>>)
However, if I change the assertThat
method signature to:
public static <T> void assertThat(T result, Matcher<? extends T> matcher)
Then the compilation works.
So three questions:
assertThat
method to Matcher<? extends T>
? Are there other cases that would break if you did that?assertThat
method in JUnit? The Matcher
class doesn't seem to require it, since JUnit calls the matches method, which is not typed with any generic, and just looks like an attempt to force a type safety which doesn't do anything, as the Matcher
will just not in fact match, and the test will fail regardless. No unsafe operations involved (or so it seems).For reference, here is the JUnit implementation of assertThat
:
public static <T> void assertThat(T actual, Matcher<T> matcher) {
assertThat("", actual, matcher);
}
public static <T> void assertThat(String reason, T actual, Matcher<T> matcher) {
if (!matcher.matches(actual)) {
Description description = new StringDescription();
description.appendText(reason);
description.appendText("\nExpected: ");
matcher.describeTo(description);
description
.appendText("\n got: ")
.appendValue(actual)
.appendText("\n");
throw new java.lang.AssertionError(description.toString());
}
}
First - I have to direct you to http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html -- she does an amazing job.
The basic idea is that you use
<T extends SomeClass>
when the actual parameter can be SomeClass
or any subtype of it.
In your example,
Map<String, Class<? extends Serializable>> expected = null;
Map<String, Class<java.util.Date>> result = null;
assertThat(result, is(expected));
You're saying that expected
can contain Class objects that represent any class that implements Serializable
. Your result map says it can only hold Date
class objects.
When you pass in result, you're setting T
to exactly Map
of String
to Date
class objects, which doesn't match Map
of String
to anything that's Serializable
.
One thing to check -- are you sure you want Class<Date>
and not Date
? A map of String
to Class<Date>
doesn't sound terribly useful in general (all it can hold is Date.class
as values rather than instances of Date
)
As for genericizing assertThat
, the idea is that the method can ensure that a Matcher
that fits the result type is passed in.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With