Как я могу отфильтровать список в Java, используя функцию предиката? - PullRequest
0 голосов
/ 10 июля 2011

Рассмотрим следующий код:

ICondition searchCondition, scopeCondition...
List<ICondition> filtered = CollectionUtil.filter(
    Arrays.asList(searchCondition, scopeCondition),
    CollectionUtil.isNonNull);

Не удается скомпилировать:

"Метод filter(Collection<T>, CollectionUtil.Predicate<T>) в типе CollectionUtil не применим для аргументов (List<ICondition>, CollectionUtil.Predicate<Object>) "

Все хорошо, если я определяю ICondition -специфический isNonNull() предикат, но это глупо, и я не понимаю, что не так или как это исправить.1016 * Вот мои служебные функции:

public interface Predicate<T> 
{
    boolean apply(T type); 
}

public static <T> List<T> filter(Collection<T> target, Predicate<T> predicate) 
{
    target = Collections.unmodifiableCollection(target);
    List<T> result = new ArrayList<T>();
    for (T element: target) {
        if (predicate.apply(element)) {
            result.add(element);
        }
    }
    return result;
}


// This predicate works as expected.
public static CollectionUtil.Predicate<String> isStringNonBlank = new CollectionUtil.Predicate<String>() {
    public boolean apply (String item) {
        return !StringUtils.isBlank(item);
    }
};

// This predicate looks fine, but fails in usage.
public static CollectionUtil.Predicate<Object> isNonNull = new CollectionUtil.Predicate<Object>() {
    public boolean apply (Object item) {
        return null != item;
    }
};

Почему я не могу использовать второй предикат с filter()?

Ответы [ 2 ]

7 голосов
/ 10 июля 2011

Похоже, что параметр predicate вашей filter функции неправильно контравариантный .Попробуйте переписать его следующим образом:

public static <T> List<T> filter(Collection<? extends T> source,
                                 Predicate<? super T> predicate)
{
  final List<T> result = new ArrayList<T>(source.size());
  for (T element: source)
    if (predicate.apply(element))
      result.add(element);
  return result;
}

Это говорит о том, что до тех пор, пока функция предиката готова принимать тип не уже , чем тип T, вызывая его с экземпляромтип T (или какой-то другой тип, производный от T) будет работать нормально.

2 голосов
/ 10 июля 2011

Попробуйте обобщить isNonNull:

private static class IsNonNullPredicate<T> implements Predicate<T> {
    public boolean apply(T item) {
        return null != item;
    }
}

Теперь вы можете вернуть его с помощью универсального метода в вашем классе утилит вместо константы.

public <T> Predicate<T> isNonNull() {
    return new IsNonNullPredicate<T>();
}

В качестве альтернативы просто выполнитеснимите флажок приведения к сохраненному экземпляру вместо создания нового каждый раз:

private final Predicate isNotNullPredicate = new IsNonNullPredicate();
public <T> Predicate<T> isNonNull() {
    return (Predicate<T>) isNotNullPredicate;
}

Это то, что класс Collections в библиотеке Java Collections делает для поддержки обобщений в своих служебных методах.До 1.5 было Collections.EMPTY_LIST, которое после добавления генериков вернуло бы List<Object>.Однако это не вернуло бы соответственно обобщенный список, поэтому был добавлен Collections.emptyList() для возврата List любого типа, который бы соответствовал вызывающему контексту.

...