Ich habe eine Frage zur Verwendung der Function.identity()
Methode.
Stellen Sie sich den folgenden Code vor:
Arrays.asList("a", "b", "c")
.stream()
.map(Function.identity()) // <- This,
.map(str -> str) // <- is the same as this.
.collect(Collectors.toMap(
Function.identity(), // <-- And this,
str -> str)); // <-- is the same as this.
Gibt es einen Grund, warum Sie Function.identity()
anstelle von str->str
(oder umgekehrt) verwenden sollten. Ich denke, dass die zweite Option besser lesbar ist (Geschmackssache natürlich). Aber gibt es einen "echten" Grund, warum man bevorzugt werden sollte?
java
lambda
java-8
java-stream
Przemysław Głębocki
quelle
quelle
t -> t
einfach vorziehen , weil es prägnanter ist.Antworten:
Ab der aktuellen JRE-Implementierung
Function.identity()
wird immer dieselbe Instanz zurückgegeben, während bei jedem Auftreten vonidentifier -> identifier
nicht nur eine eigene Instanz erstellt wird, sondern sogar eine eigene Implementierungsklasse vorhanden ist. Weitere Details finden Sie hier .Der Grund dafür ist, dass der Compiler eine synthetische Methode generiert, die den Trivialkörper dieses Lambda-Ausdrucks enthält (im Fall von
x->x
äquivalent zureturn identifier;
) und die Laufzeit anweist, eine Implementierung der funktionalen Schnittstelle zu erstellen, die diese Methode aufruft. Die Laufzeit sieht also nur unterschiedliche Zielmethoden und die aktuelle Implementierung analysiert die Methoden nicht, um herauszufinden, ob bestimmte Methoden gleichwertig sind.Wenn Sie also
Function.identity()
anstelle vonx -> x
verwenden, sparen Sie möglicherweise Speicherplatz, aber das sollte Ihre Entscheidung nicht beeinflussen, wenn Sie der Meinung sind, dass diesx -> x
besser lesbar ist alsFunction.identity()
.Sie können auch berücksichtigen, dass die synthetische Methode beim Kompilieren mit aktivierten Debug-Informationen ein Zeilen-Debug-Attribut aufweist, das auf die Quellcode-Zeile (n) verweist, in denen sich der Lambda-Ausdruck befindet. Daher haben Sie die Möglichkeit, die Quelle einer bestimmten
Function
Instanz beim Debuggen zu finden . Wenn Sie dagegen auf die Instanz stoßen, dieFunction.identity()
beim Debuggen einer Operation zurückgegeben wurde, wissen Sie nicht, wer diese Methode aufgerufen und die Instanz an die Operation übergeben hat.quelle
x -> x
Frame enthält. Schlagen Sie vor, den Haltepunkt auf dieses Lambda zu setzen? Normalerweise ist es nicht so einfach, den Haltepunkt in das Lambda mit einem Ausdruck zu setzen (zumindest in Eclipse) ...Function.identity()
diese Informationen verloren. Dann kann die Aufrufkette in einfachen Fällen hilfreich sein, aber denken Sie beispielsweise an eine Multithread-Auswertung, bei der sich der ursprüngliche Initiator nicht in der Stapelverfolgung befindet…new
.new Foo(…)
Garantien, um eine neue Instanz des genauen Typs zu erstellenFoo
, währendFoo.getInstance(…)
möglicherweise eine vorhandene Instanz von (einem Subtyp von) zurückgegeben wirdFoo
…In Ihrem Beispiel gibt es keinen großen Unterschied zwischen
str -> str
undFunction.identity()
da es intern einfach istt->t
.Aber manchmal können wir nicht verwenden,
Function.identity
weil wir a nicht verwenden könnenFunction
. Schauen Sie hier:Dies wird gut kompiliert
aber wenn Sie versuchen zu kompilieren
Sie erhalten einen Kompilierungsfehler, da
mapToInt
erwartet wirdToIntFunction
, was nicht damit zusammenhängtFunction
. HatToIntFunction
auch keineidentity()
Methode.quelle
i -> i
mitFunction.identity()
in einem Compiler - Fehlern führen wird.mapToInt(Integer::intValue)
.mapToInt(i -> i)
es eine Vereinfachung von istmapToInt( (Integer i) -> i.intValue())
. Verwenden Sie die Version, die Sie für klarer halten. Für michmapToInt(i -> i)
zeigt dies besser die Absichten dieses Codes.i -> i
wie eine Identitätsfunktion aussieht, die es in diesem Fall nicht ist.i -> i
da mein Ziel darin besteht, Integer int zuzuordnen (wasmapToInt
sehr gut nahelegt), dieintValue()
Methode nicht explizit aufzurufen . Wie dieses Mapping erreicht wird, ist nicht wirklich so wichtig. Lassen Sie uns also einfach zustimmen, nicht zuzustimmen, aber danke, dass Sie auf mögliche Leistungsunterschiede hingewiesen haben, muss ich mir das eines Tages genauer ansehen.Aus der JDK-Quelle :
Also nein, solange es syntaktisch korrekt ist.
quelle
t->t
im Quellcode kann ein Objekt erzeugen und die Implementierung vonFunction.identity()
ist ein Vorkommen. Alle aufgerufenen Aufrufseitenidentity()
teilen also dieses eine Objekt, während alle Sites, die explizit den Lambda-Ausdruck verwendent->t
, ihr eigenes Objekt erstellen. Die MethodeFunction.identity()
ist in keiner Weise speziell. Wenn Sie eine Factory-Methode erstellen, die einen häufig verwendeten Lambda-Ausdruck kapselt, und diese Methode aufrufen, anstatt den Lambda-Ausdruck zu wiederholen, können Sie angesichts der aktuellen Implementierung Speicherplatz sparen .t->t
Objekts bei jedem Aufruf der Methode optimiert und bei jedem Aufruf der Methode dasselbe wiederverwertet.invokedynamic
Befehl ein, der bei seiner ersten Ausführung durch Ausführen einer sogenannten Bootstrap-Methode verknüpft wird, die sich bei Lambda-Ausdrücken in der befindetLambdaMetafactory
. Diese Implementierung beschließt, ein Handle an einen Konstruktor, eine Factory-Methode oder einen Code zurückzugeben, der immer dasselbe Objekt zurückgibt. Möglicherweise wird auch entschieden, einen Link zu einem bereits vorhandenen Handle zurückzugeben (was derzeit nicht der Fall ist).