泛型地狱:Hamcrest匹配器作为方法参数 [英] Generics hell: hamcrest matcher as a method parameter

查看:85
本文介绍了泛型地狱: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)));

...因为所有这些工厂静态方法都产生符合方法签名的匹配器匹配器< ;?然而,我相信一个接受一个对象的Iterable的匹配器应该被<$ c>所接受。 $ c> matchIt()方法:

  matchIt(everyItem(anything())) ; 

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

$ p $ public boolean matchIt(final Matcher<

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

 实际参数匹配器< Iterable< String>>> ;不能转换为匹配器?超级列表<?超级字符串>>通过方法调用转换

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

编辑
@rlegendi我的意图是为客户提供一个接口来添加和执行有关列表的谓词。这是 matchIt()方法。调用 matchIt(anything())在这种情况下是合理的,客户端想知道列表是否是任何东西。调用 matchIt(empty())表示客户端想知道列表是否为空。对于 matchIt(everyItem(equalToIgnoringCase(item)))和 matchIt(hasItem(item))



我的目标是获得最好的匹配方法签名matchIt()方法签名可能。 匹配器<?超级列表< String>>< / code>适用于所有以前的场景。然而,我相信应该允许客户端添加 Matcher< Iterable< Object>> 匹配器(例如 matchIt(everyItem(notNullValue ))在这里非常有意义,客户希望知道列表中的每个String项是否不为null)。

然而,我可以'找到正确的签名, matchIt(Matcher< ;? super List< ;? super String>>)不适用于 everyItem(notNullValue() );



我使用Hamcrest 1.3。



编辑2:



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


$ b

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



然而,我不明白为什么我不能做 Matcher<超级列表<超级字符串>> m1 = m; 。 eems Matcher< Iterable< Object>> 不是匹配器<?超级列表<?超级字符串>> 但我不明白为什么。



我甚至不能执行 Matcher< ?超级列表<>>> m1 = m; Matcher< Iterable< Object>> 不是匹配器<?超级列表<>> ?为什么?

解决方案


然而,我相信一个匹配器接受一个可重用的对象
应该被matchIt()方法接受。

不,那是不正确的。现在让我们考虑 List ,而不是 Iterable 。所以你有一个 Matcher< List< Object>> ,它的匹配方法需要一个列表与LT;对象> 。现在,这需要一个 List< String> ?不,你可能已经知道为什么 - 因为它可以将 Object 类型的对象添加到列表中。



现在,我知道在命名类 Matcher 时,你期望匹配方法是只读的,并且不会改变赋予它的列表。但是,这并不能保证。如果确实没有向列表添加任何内容,则匹配器的正确类型将是 Matcher< List<>> ,其中(1)不允许匹配方法,除了 null 之外,其他任何东西都会添加到列表中,(2)将允许匹配方法来获取任何类型的列表。



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


So, let's have a list of strings and a function that takes a Hamcrest matcher and returns a result of the matches() method of the provided matcher:

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

So far so good. Now I can easily call:

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

...since all of these factory static methods produce a matcher that fits the method signature Matcher<? super List<String>>.

However, I believe a matcher that accepts an Iterable of objects should be accepted by the matchIt() method as well:

matchIt(everyItem(anything()));

So I naively changed the matchIt() method signature:

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

But it doesn't work at all. Not only it doesn't accept everyItem(anything()), it doesn't even accept the previously correct everyItem(equalToIgnoringCase("item")) saying (1.7.0_05 compiler version):

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

What the? So what's wrong here? Is it the matchIt() method signature or is the everyItem() Hamcrest signature designed wrongly? Or is it just the Java generics system being beyond repair? Thanks a lot for you comments!

EDIT @rlegendi my intention here is to provide an interface for the client to add and execute predicates about the list. That's the matchIt() method. Calling matchIt(anything()) makes sense in this scenario, the client wants to know whether the list is anything. Calling matchIt(empty()) means the client wants to know whether the list is empty. Vice versa for matchIt(everyItem(equalToIgnoringCase("item"))) and matchIt(hasItem("item")).

My goal here is to have a best matchIt() method signature possible. The Matcher<? super List<String>> works fine for all of the previous scenarios. However, I believe the client should be allowed to add Matcher<Iterable<Object>> matchers as well (for example matchIt(everyItem(notNullValue()) makes perfect sense here, the client wants to know whether every String item of the list is not null).

However I can't find the right signature, matchIt(Matcher<? super List<? super String>>) doesn't work for everyItem(notNullValue());

I use Hamcrest 1.3.

EDIT 2:

I believe I have found my root misunderstanding.

The everyItem(anything()) expression return an object of type Matcher<Iterable<Object>>. So I can do easily Matcher<Iterable<Object>> m = everyItem(anything());

However what I don't understand is why I can't do Matcher<? super List<? super String>> m1 = m;. It seems that Matcher<Iterable<Object>> is not Matcher<? super List<? super String>> but I don't get it why.

I can't even do Matcher<? super List<?>> m1 = m;. Matcher<Iterable<Object>> is not Matcher<? super List<?>>? Why?

解决方案

However, I believe a matcher that accepts an Iterable of objects should be accepted by the matchIt() method as well

No, that is not correct. Instead of Iterable, let's consider List for the moment. So you have a Matcher<List<Object>>, and its matches method takes a List<Object>. Now, would this take a List<String>? No. And you probably already know why -- because it could add an object of type Object into the list.

Now, I know that in naming the class Matcher, you expect the matches method to be read-only, and not mutate the list given to it. However, there is no guarantee of that. If indeed it does not add anything to the list, the correct type for the matcher would be Matcher<List<?>>, which (1) does not allow the matches method to add anything to the list except null, and (2) will allow the matches method to take a list of any type.

I believe that your current method signature public boolean matchIt(final Matcher<? super List<String>> matcher) already allows Matcher<List<?>> (or Matcher<Iterable<?>>).

这篇关于泛型地狱:Hamcrest匹配器作为方法参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆