泛型地狱:hamcrest matcher作为方法参数

2022-09-03 02:57:18

因此,让我们有一个字符串列表和一个函数,该函数采用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>>

但是,我相信接受对象可迭代对象的匹配器也应该被该方法接受:matchIt()

matchIt(everyItem(anything()));

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

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

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

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

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

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

我的目标是尽可能有一个最好的方法签名。适用于上述所有方案。但是,我认为也应该允许客户端添加匹配器(例如,在这里非常有意义,客户端想知道列表中的每个字符串项是否不为空)。matchIt()Matcher<? super List<String>>Matcher<Iterable<Object>>matchIt(everyItem(notNullValue())

但是我找不到正确的签名,不适用于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<?>>


答案 1

但是,我相信接受对象可迭代对象的匹配器也应该被 matchIt() 方法接受

不,这是不正确的。让我们暂时考虑一下,而不是 。所以你有一个 ,它的方法需要一个 .现在,这需要一个?不。你可能已经知道为什么了 -- 因为它可以在列表中添加一个类型的对象。IterableListMatcher<List<Object>>matchesList<Object>List<String>Object

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

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


答案 2

这有什么问题吗?

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

推荐