stream reduction incompatible types

2019-05-11 05:21发布

问题:

I'm trying to create a finder which takes several predicates and reduces them:

public static <T extends BusinessInterface> Collection<T> findOr(
    Context pContext, Class<T> pClass, Predicate<? super T>... pPredicates) {
  Predicate<? super T> lReducedPredicate =
      Arrays.asList(pPredicates).stream().reduce(Predicate::or).orElse(r -> false);
  return find(pContext, pClass, lReducedPredicate);
}

Unfortunately I get following compiler error:

Predicate lReducedPredicate = Arrays.asList(pPredicates).stream().reduce(Predicate::or).orElse(r -> false); incompatible types: Predicate cannot be converted to Predicate where T is a type-variable: T extends BusinessInterface declared in method findOr(Context,Class,Predicate...) where CAP#1,CAP#2 are fresh type-variables: CAP#1 extends Object super: T from capture of ? super T CAP#2 extends Object super: T from capture of ? super T

I get not errors in Eclipse and I have no idea what is going wrong.

Any help is really appreciated :).

回答1:

One way to solve this is using

public static <T extends BusinessInterface> Collection<T> findOr(
    Context pContext, Class<T> pClass, Predicate<? super T>... pPredicates) {
  Predicate<? super T> lReducedPredicate = Arrays.asList(pPredicates).stream()
    .reduce((a,b) -> t -> a.test(t) || b.test(t)).orElse(r -> false);
  return find(pContext, pClass, lReducedPredicate);
}

While you can’t invoke the or method on a Predicate<? super T> instance with another Predicate<? super T> as argument, you can create the same function, or would return, yourself, as passing a T instance to either test method is valid.



回答2:

Your code works for me in intellij, but not in ideone, which is strange.

The reason for the compiler error is that you're effectively trying to do this:

Predicate<? super T> a = null;
Predicate<? super T> b = null;
a.or(b);  // Compiler error!

Because of the separate wildcards on both a and b, the compiler can't guarantee that the type bounds are compatible, because those wildcards are not necessarily the same.

Anyway, try this one weird trick:

Arrays.asList(pPredicates)
    .stream()
    .map(a -> a)  // Here!
    .reduce(Predicate::or)
    .orElse(r -> false);

Inserting this line allows it to cast to the type which matches the type constraints.