web-dev-qa-db-fra.com

Pourquoi .forEach (val -> list.add ()) compile alors que .forEach (val -> true) ne le fait pas?

Il vaut mieux exprimer ce comportement dans le code:

List<Integer> list= new ArrayList<>();
Stream.of(1,2,3).forEach(i -> list.add(1));  // COMPILES

Stream.of(1,2,3).forEach(i -> true);  // DOES NOT COMPILE!

forEach(...) accepte Consumer, mais pourquoi le premier exemple est-il compilé si l'interface List a la signature suivante boolean add(E e)? alors que la seconde donne:

type de retour incorrect dans l'expression lambda: le booléen ne peut pas être converti en vide

8
Leonid Dashko

Bien que vous cherchiez peut-être juste 

Stream.of(1,2,3).forEach(list::add); // adding all to `list`

pourquoi le premier exemple est-il compilé si l'interface List a la suite signature booléenne ajouter (E e)

Principalement parce que le type de retour de la méthode est ignoré lors du premier appel. C'est ce qu'il se développe pour:

Stream.of(1,2,3).forEach(new Consumer<Integer>() {
    @Override
    public void accept(Integer i) {
        list.add(1); // ignored return type
    }
});  // COMPILES

D'autre part, l'autre représentation lambda ressemble plus à une Predicate (qui est aussi une FunctionalInterface) représentée comme renvoyant true toujours à partir de sa méthode test. Si vous essayez même de le représenter sous la forme d'une Consumer, il se pourrait bien que 

Stream.of(1,2,3).forEach(new Consumer<Integer>() {
    @Override
    public void accept(Integer i) {
        return true; // you can correlate easily now why this wouldn't compile 
    }
});  // DOES NOT COMPILE!

Pour ajouter au dimensionnement via un commentaire de Brian

Java vous permet d'appeler une méthode et d'ignorer la valeur renvoyée (une expression d'invocation de méthode sous forme d'instruction). Puisque nous permettons cela à l’invocation, nous l’autorisons également lors de l’adaptation d’une méthode à un interface fonctionnelle dont les arguments sont compatibles mais le fonctionnel l'interface est vide.

Edit: Pour le mettre dans ses propres mots aussi proches de la langue spécifiée :

Plus précisément, list.add(x) est une expression statement, et est donc compatible avec le vide. true n'est pas une expression de déclaration, et donc pas vide compatible. forEach(Consumer) nécessite un lambda compatible.

11
nullpointer

La règle spéciale de compatibilité avec vide (JLS) indique qu'un lambda qui renvoie vide peut accepter une instruction dont le retour est non nul. Dans ce cas, le retour est jeté. d'où cela compile.

Stream.of(1,2,3).forEach(i -> list.add(1)); // return value of add is ignored

Pour Word un peu différemment en citant Java-8 dans Action book:

Si un lambda a pour expression une expression, son nom est compatible avec un descripteur de fonction qui retourne void (à condition que le paramètre list soit également compatible). Par exemple, les deux lignes suivantes sont légal même si la méthode add d’une liste renvoie un booléen et non vide comme prévu dans le contexte consommateur (T -> vide):

// Predicate has a boolean return
 Predicate<String> p = s -> list.add(s); 
// Consumer has a void return 
Consumer<String> b = s -> list.add(s);

En ce qui concerne le deuxième exemple, il s'agit de explicitement essayer de renvoyer un booléen où ce n'est pas prévu, donc impossible.

Stream.of(1,2,3).forEach(i -> true);  

en d'autres termes:

Stream.of(1,2,3).forEach(i -> {return true;});

Comme vous le savez déjà, forEach prend un consommateur. Par conséquent, toute tentative visant à renvoyer "explicitement" une boolean devrait donner une erreur de compilation.

0
Aomine

forEach() en tant que Consommateur en tant que seul et unique paramètre. Par conséquent, vous devez donner une implémentation de la méthode accept() qui renvoie void. Ainsi, dans le deuxième exemple, vous implémentez une méthode avec l'assinature static boolean method(int) donnée.

0
chriptus13