web-dev-qa-db-de.com

Warum kompiliert .forEach (val -> list.add ()), wohingegen .forEach (val -> true) dies nicht tut?

Es ist besser, dieses Verhalten im Code auszudrücken:

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(...) akzeptiert Consumer, aber warum kompiliert das erste Beispiel, wenn das Listen-Interface die folgende Signatur boolean add(E e) hat? während die zweite ergibt:

falscher Rückgabetyp in Lambda-Ausdruck: Boolean kann nicht in .__ konvertiert werden. Leere

8
Leonid Dashko

Obwohl Sie vielleicht gerade suchen 

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

warum wird das erste Beispiel kompiliert, wenn für die Listenschnittstelle Folgendes festgelegt ist Unterschrift boolescher Wert (E e)

Hauptsächlich, weil der Rückgabetyp der Methode beim ersten Aufruf ignoriert wird. Dies ist, was es erweitert:

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

Auf der anderen Seite ähnelt die andere Lambda-Darstellung eher einer Predicate (die auch eine FunctionalInterface ist), die so dargestellt wird, dass true immer von ihrer test-Methode zurückgegeben wird. Wenn Sie sogar versuchen, es als Consumer darzustellen, sieht es möglicherweise so aus 

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!

Zur Design-Basis durch einen Kommentar von Brian hinzufügen

Mit Java können Sie eine Methode aufrufen und den Rückgabewert ignorieren (ein Methodenaufrufsausdruck als Anweisung). Da erlauben wir das bei Beim Aufruf erlauben wir dies auch, wenn Sie eine Methode an eine .__ anpassen. funktionale Schnittstelle, deren Argumente kompatibel sind, jedoch die funktionale Schnittstelle ist nichtig.

Edit: Um es in seine eigenen Wörter so nahe an die Sprachspezifikation zu setzen :

Genauer gesagt ist list.add(x) ein statement-Ausdruck und daher ist void-kompatibel. true ist kein Anweisungsausdruck und daher nicht void-kompatibel. forEach(Consumer) erfordert ein voidkompatibles Lambda.

11
nullpointer

Die Spezielle Void-Kompatibilitätsregel (JLS) besagt, dass ein Lambda, das void zurückgibt, eine Anweisung akzeptieren kann, deren Rückkehr nicht-void ist. In diesem Fall wird die Rückgabe verworfen. daher das kompiliert.

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

Um es etwas anders zu formulieren, indem Sie Java-8 in Action book zitieren:

Wenn ein Lambda einen Anweisungsausdruck als Hauptteil hat, ist es kompatibel mit einem Funktionsdeskriptor, der void zurückgibt (sofern der Parameter list auch kompatibel ist). Zum Beispiel sind die beiden folgenden Zeilen legal, auch wenn die Methode add einer Liste einen Booleschen Wert und nicht .__ zurückgibt. Void wie erwartet im Consumer-Kontext (T -> Void):

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

Im zweiten Beispiel versucht dies explizit, ein boolesches zurückzugeben, bei dem dies nicht erwartet wird und daher nicht möglich ist.

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

mit anderen Worten:

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

Wie Sie bereits wissen, benötigt forEach einen Consumer. Wenn Sie also versuchen, "explizit" eine boolean zurückzugeben, sollte & einen Kompilierungsfehler ergeben.

0
Aomine

forEach() als Consumer als einzigem Parameter. Daher müssen Sie eine Implementierung der Methode accept() angeben, die void zurückgibt. Im zweiten Beispiel implementieren Sie eine Methode mit der angegebenen Assinatur static boolean method(int).

0
chriptus13