How to correctly combine the predicates filter?

Viewed 91

I want to create method for combine Predicate::and in one predicate and submit it on input list. I have code:

public static List<?> getFilteredList(Collection<?> collection, Collection<Predicate<?>> filters) {
    return collection.stream()
            .filter(filters.stream().reduce(Predicate::and).orElse(t -> true))
            .collect(Collectors.toList());

}

But compiler says that there is a error in Predicate::and Incompatible types: Predicate<capture of ?> is not convertible to Predicate<? super capture of ?>

How to fix it?

1 Answers

As it stands, you could be providing completely incompatible predicates:

Collection<Predicate<?>> predicates = 
    List.of((String s) -> s.isEmpty(), (Integer i) -> i >= 0)

Combining those doesn't make sense.

You need to provide predicates which are compatible with the collection elements:

public static <T> List<T> getFilteredList(
    Collection<? extends T> collection,
    Collection<? extends Predicate<? super T>> predicates) {

  Predicate<T> combined = predicates.stream().reduce(t -> true, Predicate::and, Predicate::and);
  return collection.stream()
      .filter(combined)
      .collect(Collectors.toList());
}

I went to town a bit on the wildcards here. You could do it in a much simpler way, at the expense of flexibility in the arguments it will accept:

public static <T> List<T> getFilteredList(
    Collection<T> collection,
    Collection<Predicate<T>> predicates) {

  Predicate<T> combined = predicates.stream().reduce(t -> true, Predicate::and);
  return collection.stream()
      .filter(combined)
      .collect(Collectors.toList());
}
Related