Überspringen Methodenverweise den Overhead der Lambda-Hülle? Könnten sie in der Zukunft?
Gemäß dem Java-Tutorial zu Methodenreferenzen :
Manchmal ... ruft ein Lambda-Ausdruck nur eine vorhandene Methode auf. In diesen Fällen ist es oft klarer, die vorhandene Methode namentlich zu bezeichnen. Mit Methodenreferenzen können Sie dies tun. Sie sind kompakte, leicht zu lesende Lambda-Ausdrücke für Methoden, die bereits einen Namen haben.
Ich bevorzuge die Lambda-Syntax aus mehreren Gründen der Methodenreferenzsyntax:
Lambdas sind klarer
Trotz der Behauptungen von Oracle finde ich die Lambda-Syntax besser lesbar als die Objektmethodenreferenz, da die Methodenreferenzsyntax nicht eindeutig ist:
Bar::foo
Rufen Sie eine statische Methode mit einem Argument für die Klasse x auf und übergeben Sie sie x?
x -> Bar.foo(x)
Oder rufen Sie eine Instanzmethode mit null Argumenten für x auf?
x -> x.foo()
Die Methodenreferenzsyntax könnte für beide stehen. Es verbirgt, was Ihr Code tatsächlich tut.
Lambdas sind sicherer
Wenn Sie Bar :: foo als Klassenmethode referenzieren und Bar später eine gleichnamige Instanzmethode hinzufügt (oder umgekehrt), wird Ihr Code nicht mehr kompiliert.
Sie können Lambdas konsequent verwenden
Sie können jede Funktion in ein Lambda einschließen, sodass Sie überall dieselbe Syntax konsistent verwenden können. Die Methodenreferenzsyntax funktioniert nicht bei Methoden, die primitive Arrays annehmen oder zurückgeben, geprüfte Ausnahmen auslösen oder denselben Methodennamen haben, der als Instanz und statische Methode verwendet wird (da die Methodenreferenzsyntax nicht eindeutig ist, welche Methode aufgerufen wird). . Sie funktionieren nicht, wenn Sie Methoden mit der gleichen Anzahl von Argumenten überladen haben, aber Sie sollten das sowieso nicht tun (siehe Punkt 41 von Josh Bloch), daher können wir das nicht mit Methodenreferenzen vergleichen.
Fazit
Wenn es dafür keine Leistungseinbußen gibt, bin ich versucht, die Warnung in meiner IDE auszuschalten und die Lambda-Syntax konsistent zu verwenden, ohne den gelegentlichen Methodenverweis in meinen Code zu streuen.
PS
Weder hier noch dort, aber in meinen Träumen sehen Objektmethodenreferenzen eher so aus und wenden ohne Lambda-Wrapper eine Aufrufdynamik auf die Methode direkt auf das Objekt an:
_.foo()
.map(_.acceptValue())
: Wenn ich mich nicht irre, sieht das der Scala-Syntax sehr ähnlich. Vielleicht versuchen Sie nur, die falsche Sprache zu verwenden.System.out::println
an dieforEach()
ganze Zeit ...?Antworten:
In vielen Szenarien halte ich Lambda und Methodenreferenz für äquivalent. Das Lambda umschließt das Aufrufziel jedoch mit dem deklarierenden Schnittstellentyp.
Zum Beispiel
Sie werden sehen, dass die Konsole das Stacktrace ausgibt.
In
lambda()
der aufrufenden Methodetarget()
gibtlambda$lambda$0(InvokeTest.java:20)
es nachvollziehbare Zeileninformationen. Das ist natürlich das Lambda, das Sie schreiben. Der Compiler generiert eine anonyme Methode für Sie. Und dann ist der Aufrufer der Lambda-Methode so etwas wieInvokeTest$$Lambda$2/1617791695.run(Unknown Source)
derinvokedynamic
Aufruf in JVM. Dies bedeutet, dass der Aufruf mit der generierten Methode verknüpft ist .In
methodReference()
ist der Methodenaufruftarget()
direkt derInvokeTest$$Lambda$1/758529971.run(Unknown Source)
, dh der Aufruf ist direkt mit derInvokeTest::target
Methode verbunden .Fazit
Vergleichen Sie vor allem mit der Methodenreferenz, da die Verwendung des Lambda-Ausdrucks nur noch einen Methodenaufruf für die Erzeugungsmethode von Lambda verursacht.
quelle
Es geht nur um die Metafactory
Erstens müssen die meisten Methodenreferenzen nicht von der Lambda-Metafabrik entzuckert werden, sondern werden einfach als Referenzmethode verwendet. Im Abschnitt "Lambda-Körperzuckerung" des Artikels " Übersetzung von Lambda-Ausdrücken " ("TLE"):
Dies wird weiter unten in TLEs "The Lambda Metafactory" hervorgehoben:
Eine statische (
Integer::sum
) oder unbeschränkte Instanzmethode (Integer::intValue
) ist die "einfachste" oder "bequemste" in dem Sinne, dass sie von einer "Fast-Path" -Metafactory-Variante ohne Desugaring optimal gehandhabt werden kann . Auf diesen Vorteil wird in den "Metafactory-Varianten" von TLE besonders hingewiesen:Natürlich muss eine instanzerfassende Methodenreferenz (
obj::myMethod
) die begrenzte Instanz als Argument für das Methodenhandle für den Aufruf bereitstellen, was bedeuten kann, dass die Verwendung von 'Brücken'-Methoden desugariert werden muss.Fazit
Ich bin mir nicht ganz sicher, auf was für einen Lambda-Wrapper Sie hinweisen, aber obwohl das Endergebnis der Verwendung Ihrer benutzerdefinierten Lambdas oder Methodenreferenzen dasselbe ist, scheint die Art und Weise , in der sie erreicht werden, ganz anders zu sein kann in Zukunft anders sein, wenn das jetzt nicht der Fall ist. Daher ist es wahrscheinlich, dass Methodenreferenzen von der Metafactory optimaler gehandhabt werden können.
quelle
Bei der Verwendung von Lambda-Ausdrücken, die die Leistung beeinträchtigen können, gibt es eine schwerwiegende Konsequenz.
Wenn Sie einen Lambda-Ausdruck deklarieren, erstellen Sie einen Abschluss für den lokalen Bereich.
Was bedeutet das und wie wirkt es sich auf die Leistung aus?
Nun, ich bin froh, dass du gefragt hast. Dies bedeutet, dass jeder dieser kleinen Lambda-Ausdrücke eine kleine anonyme innere Klasse ist und dass er einen Verweis auf alle Variablen enthält, die sich im selben Bereich des Lambda-Ausdrucks befinden.
Dies bedeutet auch eine
this
Referenz der Objektinstanz und aller ihrer Felder. Abhängig vom Zeitpunkt des tatsächlichen Aufrufs des Lambdas kann dies zu einem erheblichen Ressourcenleck führen, da der Garbage Collector diese Referenzen nicht loslassen kann, solange das Objekt, das an einem Lambda anhält, noch lebt ...quelle
this
Objekt, auf das verwiesen werden könnte. Ein Lambda verliert also keine Ressourcen, indem es nur Spielraum für sie hat. es hält nur die Objekte fest, die es benötigt. Andererseits kann eine anonyme innere Klasse Ressourcen verlieren, tut dies aber nicht immer. Ein Beispiel finden Sie in diesem Code: a.blmq.us/2mmrL6v