Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

The target type of this expression must be a functional interface

I created a function to filter with multiple predicates for which I perform a logical AND for them:

@SafeVarargs
public static <T> Stream<T> filter(Stream<T> source, Predicate<T>... predicates) {
    return source.filter(Arrays.stream(predicates).reduce(predicates[0], Predicate::and));
}  

When calling:

filter(IntStream.range(0, 10).boxed(), x -> x % 2 != 0, x -> x%3 == 0).forEach(System.out::println);

It works fine and prints 3 and 9. However when I pass a single predicate such as:

filter(IntStream.range(0, 10).boxed(), x -> x % 2 != 0).forEach(System.out::println);

I get a compilation error:

The target type of this expression must be a functional interface

Why is this?

enter image description here For infos I use Eclipse Luna version 1.

like image 928
user2336315 Avatar asked Jan 04 '15 23:01

user2336315


1 Answers

This is a corner case for the compiler. In order to determine whether it should apply varargs wrapping of arguments into an array or simply pass an array, it needs to know the type of the last argument, however, in the case of a lambda expression it needs the invoked method signature to determine the type. But it’s clear what should happen as a lambda expression can never be an array type and so, javac compiles it without problems.

One acceptable work-around would be to overload the method:

@SafeVarargs
public static <T> Stream<T> filter(Stream<T> source, Predicate<T>... predicates) {
    return source.filter(
        Arrays.stream(predicates).reduce(predicates[0], Predicate::and));
}
public static <T> Stream<T> filter(Stream<T> source, Predicate<T> predicate) {
    return source.filter(predicate);
}

This would be an an acceptable work-around as it does not require any changes on the calling side while improving the efficiency for the single-arg case at the same time.


Please note that your varargs method allows zero arguments but will fail if called that way. So you should either, add another overload:

public static <T> Stream<T> filter(Stream<T> source) {
    return source;
}

or make the method safe for the zero argument case:

@SafeVarargs
public static <T> Stream<T> filter(Stream<T> source, Predicate<T>... predicates) {
    return Arrays.stream(predicates).reduce(Predicate::and)
                 .map(source::filter).orElse(source);
}
like image 180
Holger Avatar answered Oct 13 '22 23:10

Holger