给定以下示例(将 JUnit 与 Hamcrest 匹配器一起使用):
Map<String, Class<? extends Serializable>> expected = null; Map<String, Class<java.util.Date>> result = null; assertThat(result, is(expected));
这不与以下的 JUnitassertThat方法签名一起编译:
assertThat
public static <T> void assertThat(T actual, Matcher<T> matcher)
编译器错误信息是:
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>>>)
但是,如果我将assertThat方法签名更改为:
public static <T> void assertThat(T result, Matcher<? extends T> matcher)
然后编译工作。
所以三个问题:
Matcher<? extends T>
Matcher
作为参考,这里是 JUnit 的实现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()); } }
首先 - 我必须引导您访问http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html - 她做得非常出色。
基本思想是你使用
<T extends SomeClass>
当实际参数可以是SomeClass它的任何子类型时。
SomeClass
在你的例子中,
您是说expected可以包含代表任何实现的类的 Class 对象Serializable。你的结果图说它只能保存Date类对象。
expected
Serializable
Date
当您传递结果时,您将设置为T完全Map的类对象,这与任何.String``Date``Map``String``Serializable
T
Map
String``Date``Map``String``Serializable
要检查的一件事 - 你确定你想要Class<Date>而不是Date?Stringto的映射Class<Date>通常听起来不是很有用(它只能Date.class作为值而不是 的实例Date)
Class<Date>
String
Date.class
至于泛化assertThat,想法是方法可以确保Matcher传入适合结果类型的a。