web-dev-qa-db-fra.com

Le type de cible de cette expression doit être une interface fonctionnelle

J'ai créé une fonction pour filtrer avec plusieurs prédicats pour lesquels je leur effectue un ET logique:

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

Lorsque vous appelez:

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

Cela fonctionne bien et affiche 3 et 9. Cependant, lorsque je passe un prédicat unique tel que:

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

Je reçois une erreur de compilation:

The target type of this expression must be a functional interface

Pourquoi est-ce?

enter image description here Pour les infos j'utilise la version 1 d'Eclipse Luna.

14
user2336315

C'est un cas de coin pour le compilateur. Afin de déterminer si elle doit appliquer un wrapping varargs d’arguments dans un tableau ou simplement passer un tableau, elle doit connaître le type du dernier argument. Cependant, dans le cas d’une expression lambda, la signature de la méthode invoquée est nécessaire type. Mais il est clair que ce qui devrait se produire lorsqu'une expression lambda ne peut jamais être un type tableau et que, par conséquent, javac le compile sans problèmes.

Une solution acceptable serait de surcharger la méthode:

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

Ce serait une solution acceptable car elle ne nécessite aucune modification du côté appelant, tout en améliorant l'efficacité du cas à argument unique en même temps.


Veuillez noter que votre méthode varargs autorise zéro argument mais échouera si elle est appelée de cette façon. Donc, vous devriez soit ajouter une autre surcharge:

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

ou sécuriser la méthode pour le cas zéro argument:

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

Netbeans et Eclipse ont tous deux un certain nombre de bugs concernant l’analyse des expressions lambda en Java. Ils sont en train de se réparer mais, jusqu'à ce qu'ils le soient, les meilleures solutions de contournement que j'ai trouvées sont les suivantes: déclarer des types 2. si cela ne fonctionne pas, utilisez un bloc 3. si cela ne fonctionne pas, créez un objet anonyme qui implémente le prédicat/fonction, etc.

Celles-ci rendent votre code plus compliqué, mais elles sont nécessaires dans un certain nombre de situations. 

2
sprinter

Parfois, Eclipse a besoin d’un bon vieux nettoyage et d’une reconstruction de tous les projets et le problème disparaît.

0
Peteter