Целевой тип этого выражения должен быть функциональным интерфейсом

Я создал функцию для фильтрации с несколькими предикатами, для которых я выполняю логическое И для них:

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

При вызове:

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

Он отлично работает и печатает 3 и 9. Однако, когда я передаю один предикат, например:

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

Я получаю ошибку компиляции:

The target type of this expression must be a functional interface

Почему это?

enter image description here Для информации я использую Eclipse Luna версии 1.

Ответы

Ответ 1

Это угловой случай для компилятора. Чтобы определить, следует ли применять varargs, перенос аргументов в массив или просто передать массив, ему необходимо знать тип последнего аргумента, однако в случае выражения лямбда ему нужна сигнатура метода invoke, чтобы определить тип. Но его ясное, что должно происходить как выражение лямбда, никогда не может быть типом массива, и поэтому javac скомпилирует его без проблем.

Одним из приемлемых способов работы будет перегрузка метода:

@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);
}

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


Обратите внимание, что ваш метод varargs допускает нулевые аргументы, но сбой будет вызван таким образом. Поэтому вы должны добавить еще одну перегрузку:

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

или сделать метод безопасным для случая с нулевым аргументом:

@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);
}

Ответ 2

Оба Netbeans и Eclipse имеют ряд ошибок в области разбора лямбда-выражений в Java. Они медленно исправляются, но до тех пор, пока они не найдены, лучшие обходные пути, которые я нашел: 1. Объявлять типы 2. Если это не работает, используйте блок 3. Если это не сработает, создайте анонимный объект, реализующий предикат/функцию и т.д.

Это делает ваш код более грязным, но они необходимы в ряде ситуаций.

Ответ 3

Иногда Eclipse нуждается в хорошем очищении и восстановлении всех проектов, и проблема исчезает.