泛型地狱:hamcrest匹配器作为方法参数

时间:2012-10-21 18:23:02

标签: java generics hamcrest

所以,让我们有一个字符串列表和一个带有Hamcrest匹配器的函数,并返回提供的匹配器的matches()方法的结果:

public boolean matchIt(final Matcher<? super List<String>> matcher) {
    final List<String> lst = obtainListFromSomewhere();
    return matcher.matches(lst);
}

到目前为止一切顺利。现在我可以轻松打电话:

matchIt(empty());
matchIt(anything());
matchIt(hasItem("item"));
matchIt(everyItem(equalToIgnoringCase("item")));

...因为所有这些工厂静态方法都会生成一个符合方法签名Matcher<? super List<String>>的匹配器。

但是,我认为接受Iterable对象的匹配器也应该被matchIt()方法接受:

matchIt(everyItem(anything()));

所以我天真地改变了matchIt()方法签名:

public boolean matchIt(final Matcher<? super List<? super String>> matcher);

但它根本不起作用。它不仅不接受everyItem(anything()),它甚至不接受先前正确的everyItem(equalToIgnoringCase("item"))说法(1.7.0_05编译器版本):

actual argument Matcher<Iterable<String>> cannot be converted to Matcher<? super List<? super String>> by method invocation conversion

什么?那么这里有什么问题?它是matchIt()方法签名还是错误设计的everyItem() Hamcrest签名?或者只是Java泛型系统无法修复?非常感谢您的评论!

EDIT @rlegendi我的目的是为客户端提供一个接口来添加和执行有关列表的谓词。这是matchIt()方法。在这种情况下调用matchIt(anything())是有意义的,客户端想要知道列表是否是任何内容。调用matchIt(empty())表示客户端想知道列表是否为空。反之亦然matchIt(everyItem(equalToIgnoringCase("item")))matchIt(hasItem("item"))

我的目标是让最佳 matchIt()方法签名成为可能。 Matcher<? super List<String>>适用于所有以前的方案。但是,我认为应该允许客户端添加Matcher<Iterable<Object>>匹配器(例如matchIt(everyItem(notNullValue())在这里非常有意义,客户端想要知道列表中的每个String项是否都为空)。 / p>

但是我找不到合适的签名,matchIt(Matcher<? super List<? super String>>)不适用于everyItem(notNullValue());

我使用Hamcrest 1.3。

编辑2:

我相信我发现了我的误解。

everyItem(anything())表达式返回类型为Matcher<Iterable<Object>>的对象。所以我可以轻松地Matcher<Iterable<Object>> m = everyItem(anything());

然而,我不明白为什么我不能做Matcher<? super List<? super String>> m1 = m;。似乎Matcher<Iterable<Object>>不是Matcher<? super List<? super String>>,但我不明白为什么。

我甚至不能Matcher<? super List<?>> m1 = m;Matcher<Iterable<Object>>不是Matcher<? super List<?>>?为什么呢?

2 个答案:

答案 0 :(得分:4)

  

但是,我相信一个接受Iterable对象的匹配器   应该被matchIt()方法接受

不,这不正确。我们暂时不考虑Iterable,而是考虑List。因此,您有Matcher<List<Object>>,其matches方法需要List<Object>。现在,这会花费List<String>吗?不。您可能已经知道原因 - 因为它可以在列表中添加Object类型的对象。

现在,我知道在命名类Matcher时,您希望matches方法是只读的,而不是改变给定的列表。但是,不能保证这一点。如果它确实没有向列表中添加任何内容,则匹配器的正确类型为Matcher<List<?>>,其中(1)不允许matches方法向列表添加任何内容,{{1}除外}和(2)将允许null方法获取任何类型的列表。

我相信您当前的方法签名matches已经允许public boolean matchIt(final Matcher<? super List<String>> matcher)(或Matcher<List<?>>)。

答案 1 :(得分:1)

这有什么不对吗?

public boolean matchIt(final Matcher<? extends Iterable<String>> matcher);