npathai / hamcrest-optional

Matchers for JDK 8 Optional
MIT License
42 stars 3 forks source link

improve method signatures for nested generics #30

Open garretwilson opened 2 years ago

garretwilson commented 2 years ago

Thanks for your library, which I have been using for many years.

I just run into a problem with nested generics inside Optional<>. Let's say I have a method that returns an Optional<Map<Integer, BigDecimal>>:

Optional<Map<Integer, BigDecimal>> foo() {
  //TODO
}

I try to test it like this:

assertThat(foo(),
  isPresentAndIs(Map.of(1, BigDecimal.valueOf(123), 2, BigDecimal.valueOf(456))));

This doesn't compile on Eclipse 2022-06 using Java 17. It gives me:

The method assertThat(T, Matcher<? super T>) in the type MatcherAssert is not applicable for the arguments (Optional<Map<Integer,BigDecimal>>, Matcher<Optional<Map<Integer,BigDecimal>>>)

Because the second argument in assertThat() uses Matcher<? super T>, apparently it doesn't realize that one Optional<Map<Integer,BigDecimal>> is equivalent to another. (If I haven't interpreted this correctly, let me know.)

The signature to isPresentAndIs() is:

public static <T> Matcher<Optional<T>> isPresentAndIs(T operand)

If I change it to the following, the test will compile:

public static <T> Matcher<? super Optional<T>> isPresentAndIs(T operand)

I think you need a bunch of ? super sprinkled throughout your API.

garretwilson commented 2 years ago

Interestingly I can create a workaround simply by adding this method inside the test itself!

public static <T> Matcher<? super Optional<T>> isPresentAndIs(T operand) {
  return OptionalMatchers.isPresentAndIs(operand);
}

Apparently there is just some type inference that Java just can't handle without help.