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

Jan*_*dek 17 java generics hamcrest

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

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

到现在为止还挺好.现在我可以轻松打电话:

matchIt(empty());
matchIt(anything());
matchIt(hasItem("item"));
matchIt(everyItem(equalToIgnoringCase("item")));
Run Code Online (Sandbox Code Playgroud)

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

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

matchIt(everyItem(anything()));
Run Code Online (Sandbox Code Playgroud)

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

public boolean matchIt(final Matcher<? super List<? super String>> matcher);
Run Code Online (Sandbox Code Playgroud)

但它根本不起作用.它不仅不接受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
Run Code Online (Sandbox Code Playgroud)

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

编辑@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项是否都为空).

但是我找不到合适的签名,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<?>>吗?为什么?

new*_*cct 5

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

不,那是不正确的。Iterable我们List暂时考虑一下。所以你有 a Matcher<List<Object>>,它的matches方法需要 a List<Object>。现在,这需要一个吗List<String>?不。您可能已经知道原因了——因为它可以将一个类型的对象添加Object到列表中。

现在,我知道在命名类时Matcher,您希望该matches方法是只读的,并且不会改变给定它的列表。然而,这并不能保证。如果它确实没有向列表中添加任何内容,则匹配器的正确类型将是Matcher<List<?>>,其中 (1) 不允许该matches方法将任何内容添加到列表中,除了null,并且 (2) 将允许该matches方法采用以下列表:任何类型。

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