Ich habe eine Liste von Namen. In Zeile 3 musste ich das Ergebnis des Lambda-Ausdrucks auf setzen Predicate<String>
. Das Buch, das ich lese, erklärt, dass die Besetzung notwendig ist, um dem Compiler zu helfen, die passende Funktionsschnittstelle zu bestimmen.
Ich brauche jedoch keine solche Besetzung in der folgenden Zeile, weil ich nicht anrufe negate()
. Wie macht das einen Unterschied? Ich verstehe, dass negate()
hier zurückkehrt Predicate<String>
, aber macht der vorhergehende Lambda-Ausdruck nicht dasselbe?
List<String> names = new ArrayList<>();
//add various names here
names.removeIf(((Predicate<String>) str -> str.length() <= 5).negate()); //cast required
names.removeIf(((str -> str.length() <= 5))); //compiles without cast
Predicate<String> predicate = str -> str.length() <= 5; names.removeIf(predicate.negate()); names.removeIf(predicate);
negate()
, wenn es nicht in der Lage wäre, auf den Typ des Lambda-Attributs zu schließen, das Teil des Ausdrucks ist, und somit nicht feststellen kann, dass der Ausdruck a bedeutetPredicate<String>
.Antworten:
Es liegt nicht nur daran, dass Sie anrufen
negate()
. Schauen Sie sich diese Version an, die Ihrer sehr nahe kommt, aber kompiliert:Der Unterschied zwischen dieser und Ihrer Version? Es geht darum, wie Lambda-Ausdrücke ihre Typen erhalten (der "Zieltyp").
Was glaubst du, macht das?
Ihre aktuelle Antwort lautet: "Es ruft
negate()
dasPredicate<String>
durch den Ausdruck Gegebene aufstr -> 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 ruftnegate
meinen anderen funktionalen Schnittstellentyp auf ... (ja, das Beispiel wird etwas bizarr sein)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 einPredicate<String>
.Erklärung:
(str -> str.length() <= 5).negate()
macht nichtstr -> str.length() <= 5
aPredicate<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 alsSentenceExpression
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: ZieltypisierungEiner 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 verwendetPredicate<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 vonstr -> str.length() <= 5
ist eindeutigPredicate<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 sichstr -> 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 aremoveIf
benötigt wirdPredicate<String>
, und er weiß mit Sicherheit, dass der gesamte an die Methode übergebene Ausdruck a sein mussPredicate<String>
, aber er würde nicht annehmen, dass ein Lambda-Ausdruck im Argument expression a istPredicate<String>
(selbst wenn Sie ihn behandeln) als Prädikat, indem mannegate()
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).
quelle
Predicate<String>
, worauf der Compiler aufgrund der Verwendung von sichnegate
selbst, wie in dieser Antwort erläutert, überhaupt nicht schließen konnte .(str -> str.length() <= 5).negate()
nicht machenstr -> str.length() <= 5
einPredicate<String>
, auch wenn das, was Sie beabsichtigen.Ich weiß nicht, warum das so verwirrend sein muss. Dies kann mit 2 Gründen erklärt werden, IMO.
Ich werde Sie herausfinden lassen, was dies bedeutet und was die
JLS
Wörter um es herum sind. Aber im Wesentlichen sind dies wie Generika:Was ist der Typ
T
hier? Es hängt davon ab. Wenn Sie tun :Poly-Ausdrücke sollen vom Kontext abhängen, in dem sie verwendet werden. Lambda-Ausdrücke sind gleich. Nehmen wir den Lambda-Ausdruck hier isoliert :
Wenn Sie es betrachten, was ist das? Nun, es ist ein
Predicate<String>
? Aber kann A seinFunction<String, Boolean>
? Oder kann sogar seinMyTransformer<String, Boolean>
, wo:Die Auswahlmöglichkeiten sind endlos.
.negate()
direkter Aufruf eine Option sein.Ab 10_000 Meilen oben sind Sie richtig: Sie stellen dies
str -> str.length() <= 5
einerremoveIf
Methode zur Verfügung, die nur a akzeptiertPredicate
. Es gibt keine weiterenremoveIf
Methoden, daher sollte der Compiler in der Lage sein, "das Richtige zu tun", wenn Sie diese bereitstellen(str -> str.length() <= 5).negate()
.Wie kommt es, dass dies nicht funktioniert? Beginnen wir mit Ihrem Kommentar:
Es scheint, dass hier das Hauptproblem beginnt. So
javac
funktioniert es einfach nicht . Es kann nicht das Ganze nehmenstr -> str.length() <= 5).negate()
, sich selbst sagen, dass dies ein istPredicate<String>
(da Sie es als Argument verwendenremoveIf
) und dann den Teil ohne weiter zerlegen.negate()
und sehen, ob dasPredicate<String>
auch ein ist.javac
Umgekehrt muss es das Ziel kennen , um feststellen zu können, ob es legal ist anzurufennegate
oder nicht.Außerdem müssen Sie im Allgemeinen klar zwischen Polyausdrücken und Ausdrücken unterscheiden.
str -> str.length() <= 5).negate()
ist ein Ausdruck,str -> str.length() <= 5
ist ein Polyausdruck.Es könnte Sprachen geben, in denen Dinge anders gemacht werden und in denen dies möglich ist,
javac
ist einfach nicht dieser Typ.quelle
Im folgenden Beispiel
Der Ausdruck gibt true zurück. Wenn im folgenden Beispiel ohne die Besetzung,
true
weiß nichts über die Methodenegate()
Auf der anderen Seite,
Der Ausdruck wird umgewandelt
Predicate<String>
, um dem Compiler mitzuteilen, wo sich die Methode befindetnegate
. Dann ist es die Methodenegate()
, die die Auswertung tatsächlich wie folgt durchführt:Beachten Sie, dass Sie ohne die Besetzung wie folgt dasselbe Ergebnis erzielen können:
quelle
Ohne zu tief in die Dinge einzusteigen, ist das Fazit, dass das Lambda
str -> str.length() <= 5
nicht unbedingt ein ist,Predicate<String>
wie Eugene erklärte.Das heißt,
negate()
ist eine Mitgliedsfunktion vonPredicate<T>
und der Compiler kann den Aufruf nicht verwenden, umnegate()
auf den Typ zu schließen, als den das Lambda interpretiert werden soll. Selbst wenn es versucht würde, könnte es Probleme geben, denn wenn mehrere mögliche Klassennegate()
Funktionen hätten, würde es nicht wissen, welche es wählen soll.Stellen Sie sich vor, Sie sind der Compiler.
str -> str.length() <= 5
. Sie wissen, dass es Lambda ist, das einen String nimmt und einen Booleschen Wert zurückgibt, wissen aber nicht genau, welchen Typ er darstellt..negate()
aber weil Sie den Typ des Ausdrucks links vom "." Nicht kennen. Sie müssen einen Fehler melden, da Sie den Aufruf nicht zum Negieren verwenden können, um auf den Typ schließen zu können.Wäre negate als statische Funktion implementiert worden, würden die Dinge funktionieren. Zum Beispiel:
würde es Ihnen ermöglichen zu schreiben
Da die Wahl der Negationsfunktion eindeutig ist, weiß der Compiler, dass er
str -> str.length() <= 5
als a interpretiert werden muss,Predicate<T>
und kann den Typ richtig erzwingen.Ich hoffe das hilft.
quelle