-
Notifications
You must be signed in to change notification settings - Fork 376
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Matchers.everyItem() has incorrect covariance in return type in hamcrest 2.2 #289
Comments
Any news here? Is the project dead? |
Hi @Tikani, the project is still alive. I'm afraid the last few months have been quite overwhelming for me personally, so I haven't had much time to look at this. I should be able to make some time this week, though. |
Hmm... looking closer at this issue: the change was introduced in issue #40, with the justification that it followed the Guidelines for Wildcard Use. Looking at the example you provided, it appears the compiler doesn't have enough information to infer the correct type for the first argument to
to this:
This looks uglier, though it can be improved by using helper utility methods with the correct type information. |
@tumbarumba i think who claimed this change as complying with the Java wildcard usage guidelines made a mistake. Wildcards should only be used as method's generic parameters enrichers (following "producer extends, consumer super" semantics) to provide more flexible API. "Wildcarded" return type should be avoided in general (especially, return types with bounds). Guidelines for Wildcard Use states that too, after Wildcard Guidelines: block. |
Yeah, reading that page more carefully, it explicitly says:
I tried reverting #40 in my local workspace. Annoyingly, the problem with the failing type inference in public class Every<T> extends TypeSafeDiagnosingMatcher<Iterable<T>> {
...
public static <U> Matcher<Iterable<U>> everyItem(final Matcher<U> itemMatcher) {
return new Every<>(itemMatcher);
}
} Even with that change, I still get the same error for this test: @Test public void
everyItemMatchesWhenUsedWithOtherCompoundMatchers() {
Matcher<Iterable<String>> matcher = allOf(iterableWithSize(3), everyItem(containsString("ca")));
assertMatches(matcher, asList("cake", "cat", "carve"));
} I'm scratching my head at this one at the moment. It appears If anyone could help diagnose what's happening in this instance, I'd be very interested. |
@tumbarumba methods, that have generics in return types only, behave very bad with implicit type inference (iterableWithSize is an example). They are constrained only by the destination (target) type, i.e. in a very weak fashion and if target type represents not a single type, but a (sub-)set via extends/super wildcard, here we go with explicit generic declaration crutches to satisfy the capricious compiler. I think only that can be done is your first piece of advice - uglify code with helpers. :( |
@tumbarumba Would it work with the following signature? public static <U> Matcher<Iterable<U>> everyItem(final Matcher<? super U> itemMatcher) If I understand Guidelines for Wildcard Use correctly, wildcards shouldn't be used in return types, thus keeping |
@tumbarumba I tested in my project the signature I mentioned in my last comment, and it works (using Java 8). |
Does this solution break #40 |
Hey, any news from this? I tried to update junit4 source code to hamcrest 2.2 and found this exact issue. |
Hi @tumbarumba, We've run into related issues in the Spring Framework as well. See spring-projects/spring-framework#28660 (comment) for details. Specifically, in Spring's testing support we wish to add a new The problem is that the following methods in
Consequently, it is impossible for us to create a new As a side note, most of the methods in In summary, it would be great if Hamcrest could implement a consistent solution for the following issues.
|
@sbrannen imo, a correct way of dealing with variance, if you want to give flexibility to API - when an argument is contravariant and a return type is covariant. It will be great if public API is reworked this way. |
Environment: Java 11, hamcrest 2.2
Due to strange signature of everyItem():
public static <U> org.hamcrest.Matcher<java.lang.Iterable<? extends U>> everyItem(org.hamcrest.Matcher<U> itemMatcher)
it can't be combined with other matchers in allOf() methods:
allOf(iterableWithSize(5), everyItem(displayed()))
results inError:(109,28) java: no suitable method found for allOf(org.hamcrest.Matcher<java.lang.Iterable<java.lang.Object>>,org.hamcrest.Matcher<java.lang.Iterable<? extends org.openqa.selenium.WebElement>>)
Seemingly, Java's generalized target-type inference do its job wrong in this case. With hamcrest-all 1.3 this works fine because of invariant return type of everyItem():
<U> Matcher<java.lang.Iterable<U>> everyItem(Matcher<U> itemMatcher)
.The text was updated successfully, but these errors were encountered: