How can I filter a list in Java using a predicate function?
Consider the following code:
ICondition searchCondition, scopeCondition...
List<ICondition> filtered = CollectionUtil.filter(
Arrays.asList(searchCondition, scopeCondition),
CollectionUtil.isNonNull);
It fails to compile:
"The method
filter(Collection<T>, CollectionUtil.Predicate<T>)
in the typeCollectionUtil
is not applicable for the arguments(List<ICondition>, CollectionUtil.Predicate<Object>)
"
Everything is fine if I define an ICondition
-specific isNonNull()
predicate, but that's dumb and I don't understand what's wrong or how to fix it.
Here are my utility functions:
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;
}
};
Why can't I use the second predicate with filter()
?
It looks like your filter
function's predicate
parameter is not properly contravariant. Try rewriting it as follows:
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;
}
That says that so long as the predicate function is willing to accept a type no narrower than type T
, calling it with an instance of type T
(or some type further derived from T
) will work fine.
Try generifying isNonNull
:
private static class IsNonNullPredicate<T> implements Predicate<T> {
public boolean apply(T item) {
return null != item;
}
}
Now you can return it through a generic method in your util class instead of a constant.
public <T> Predicate<T> isNonNull() {
return new IsNonNullPredicate<T>();
}
Alternatively, just do an unchecked cast on a stored instance instead of creating a new one each time:
private final Predicate isNotNullPredicate = new IsNonNullPredicate();
public <T> Predicate<T> isNonNull() {
return (Predicate<T>) isNotNullPredicate;
}
This is what the Collections
class in the Java Collections library does to provide support for generics in its utility methods. Before 1.5 there was Collections.EMPTY_LIST
which after generics were added would return a List<Object>
. However, that wouldn't give back a suitably generified list so Collections.emptyList()
was added to return a List
of any type that would fit the calling context.
精彩评论