Es liegt nicht nur daran, dass Sie anrufennegate()
. Schauen Sie sich diese Version an, die Ihrer sehr nahe kommt, aber kompiliert:
Predicate<String> predicate = str -> str.length() <= 5;
names.removeIf(predicate.negate());
Der Unterschied zwischen dieser und Ihrer Version? Es geht darum, wie Lambda-Ausdrücke ihre Typen erhalten (der "Zieltyp").
Was glaubst du, macht das?
(str -> str.length() <= 5).negate()?
Ihre aktuelle Antwort lautet: "Es ruft negate()
das Predicate<String>
durch den Ausdruck Gegebene auf str -> str.length() <= 5
". Recht? Aber das ist nur so, weil du es so gemeint hast. Der Compiler weiß das nicht . Warum? Weil es alles sein könnte. Meine eigene Antwort auf die obige Frage könnte lauten: "Es ruft negate
meinen anderen funktionalen Schnittstellentyp auf ... (ja, das Beispiel wird etwas bizarr sein)
interface SentenceExpression {
boolean checkGrammar();
default SentenceExpression negate() {
return ArtificialIntelligence.contradict(explainSentence());
};
}
Ich konnte den gleichen Lambda - Ausdruck verwenden , names.removeIf((str -> str.length() <= 5).negate());
aber was bedeuten , str -> str.length() <= 5
eine zu sein , SentenceExpression
anstatt ein Predicate<String>
.
Erklärung: (str -> str.length() <= 5).negate()
macht nicht str -> str.length() <= 5
a Predicate<String>
. Und deshalb habe ich gesagt, es könnte alles sein, einschließlich meiner Funktionsschnittstelle oben.
Zurück zu Java ... Aus diesem Grund haben Lambda-Ausdrücke das Konzept des "Zieltyps", das die Mechanik definiert, unter der ein Lambda-Ausdruck vom Compiler als einen bestimmten funktionalen Schnittstellentyp verstanden wird (dh wie Sie dem Compiler helfen, dies zu wissen dass der Ausdruck Predicate<String>
eher ein als SentenceExpression
oder irgendetwas anderes ist, was es sein könnte). Vielleicht finden Sie es nützlich, durchzulesen. Was ist unter Lambda-Zieltyp und Zieltypkontext in Java zu verstehen? und Java 8: Zieltypisierung
Einer der Kontexte, in denen Zieltypen abgeleitet werden (wenn Sie die Antworten in diesen Beiträgen lesen), ist der Aufrufkontext, in dem Sie einen Lambda-Ausdruck als Argument für einen Parameter eines funktionalen Schnittstellentyps übergeben names.removeIf(((str -> str.length() <= 5)));
: Es ist nur der Lambda-Ausdruck, der als Argument für eine Methode angegeben wird, die a verwendet Predicate<String>
. Dies gilt nicht für die Anweisung, die nicht kompiliert wird.
Also mit anderen Worten ...
names.removeIf(str -> str.length() <= 5);
verwendet einen Lambda-Ausdruck an einer Stelle, an der der Argumenttyp klar definiert, wie der Typ des Lambda-Ausdrucks erwartet wird (dh der Zieltyp von str -> str.length() <= 5
ist eindeutig Predicate<String>
).
Ist (str -> str.length() <= 5).negate()
jedoch kein Lambda-Ausdruck, sondern nur ein Ausdruck, der zufällig einen Lambda-Ausdruck verwendet. Dies bedeutet, dass sich str -> str.length() <= 5
in diesem Fall nicht der Aufrufkontext befindet, der den Zieltyp des Lambda-Ausdrucks bestimmt (wie im Fall Ihrer letzten Anweisung). Ja, der Compiler weiß, dass a removeIf
benötigt wird Predicate<String>
, und er weiß mit Sicherheit, dass der gesamte an die Methode übergebene Ausdruck a sein muss Predicate<String>
, aber er würde nicht annehmen, dass ein Lambda-Ausdruck im Argument expression a ist Predicate<String>
(selbst wenn Sie ihn behandeln) als Prädikat, indem man negate()
es aufruft ; es könnte alles gewesen sein, was mit dem Lambda-Ausdruck kompatibel ist).
Aus diesem Grund müssen Sie Ihr Lambda mit einer expliziten Besetzung eingeben (oder anders, wie im ersten Gegenbeispiel, das ich gegeben habe).
Predicate<String> predicate = str -> str.length() <= 5; names.removeIf(predicate.negate()); names.removeIf(predicate);