Ich habe die Java 8- Quelle erkundet und fand diesen speziellen Teil des Codes sehr überraschend:
//defined in IntPipeline.java
@Override
public final OptionalInt reduce(IntBinaryOperator op) {
return evaluate(ReduceOps.makeInt(op));
}
@Override
public final OptionalInt max() {
return reduce(Math::max); //this is the gotcha line
}
//defined in Math.java
public static int max(int a, int b) {
return (a >= b) ? a : b;
}
Ist so Math::max
etwas wie ein Methodenzeiger? Wie wird eine normale static
Methode konvertiert IntBinaryOperator
?
TestingLambda$$Lambda$2/8460669
undTestingLambda$$Lambda$3/11043253
wurden auf zwei Anrufungen erstellt.Antworten:
Normalerweise würde man die
reduce
MethodeMath.max(int, int)
wie folgt aufrufen :Das erfordert viel Syntax, um nur aufzurufen
Math.max
. Hier kommen Lambda-Ausdrücke ins Spiel. Seit Java 8 ist es erlaubt, dasselbe auf viel kürzere Weise zu tun:Wie funktioniert das? Der Java-Compiler "erkennt", dass Sie eine Methode implementieren möchten, die zwei
int
s akzeptiert und eine zurückgibtint
. Dies entspricht den formalen Parametern der einzigen SchnittstellenmethodeIntBinaryOperator
(dem Parameter der Methode, diereduce
Sie aufrufen möchten). Der Compiler erledigt also den Rest für Sie - er geht nur davon aus, dass Sie implementieren möchtenIntBinaryOperator
.Da es jedoch
Math.max(int, int)
die formalen Anforderungen von erfülltIntBinaryOperator
, kann es direkt verwendet werden. Da Java 7 keine Syntax hat, mit der eine Methode selbst als Argument übergeben werden kann (Sie können nur Methodenergebnisse übergeben, aber niemals Methodenreferenzen), wurde die::
Syntax in Java 8 eingeführt, um Methoden zu referenzieren:Beachten Sie, dass dies vom Compiler interpretiert wird, nicht von der JVM zur Laufzeit! Obwohl für alle drei Codefragmente unterschiedliche Bytecodes erzeugt werden, sind diese semantisch gleich, sodass die letzten beiden als kurze (und wahrscheinlich effizientere) Versionen der
IntBinaryOperator
obigen Implementierung betrachtet werden können!(Siehe auch Übersetzung von Lambda-Ausdrücken )
quelle
::
heißt Methodenreferenz. Es ist im Grunde eine Referenz auf eine einzelne Methode. Das heißt, es bezieht sich auf eine vorhandene Methode mit Namen.Kurze Erläuterung :
Nachfolgend finden Sie ein Beispiel für einen Verweis auf eine statische Methode:
square
kann wie Objektreferenzen weitergegeben und bei Bedarf ausgelöst werden. Tatsächlich kann es genauso einfach als Referenz auf "normale" Methoden von Objekten verwendet werden wiestatic
solche. Zum Beispiel:Function
oben ist eine funktionale Schnittstelle . Um dies vollständig zu verstehen::
, ist es wichtig, auch funktionale Schnittstellen zu verstehen. Eine funktionale Schnittstelle ist eindeutig eine Schnittstelle mit nur einer abstrakten Methode.Beispiele für funktionelle Schnittstellen gehören
Runnable
,Callable
undActionListener
.Function
oben ist eine funktionale Schnittstelle mit nur einer Methode :apply
. Es nimmt ein Argument und erzeugt ein Ergebnis.Der Grund, warum
::
s großartig sind , ist folgender :ZB anstatt den Lambda-Körper zu schreiben
Sie können einfach tun
Zur Laufzeit
square
verhalten sich diese beiden Methoden genau gleich. Der Bytecode kann derselbe sein oder auch nicht (obwohl für den obigen Fall der gleiche Bytecode generiert wird; kompilieren Sie den obigen und prüfen Sie mitjavap -c
).Das einzige wichtige Kriterium, das erfüllt werden muss, ist: Die von Ihnen bereitgestellte Methode sollte eine ähnliche Signatur haben wie die Methode der Funktionsschnittstelle, die Sie als Objektreferenz verwenden .
Das Folgende ist illegal:
square
erwartet ein Argument und gibt a zurückdouble
. Dieget
Methode in Supplier gibt einen Wert zurück, akzeptiert jedoch kein Argument. Dies führt also zu einem Fehler.Eine Methodenreferenz bezieht sich auf die Methode einer funktionalen Schnittstelle. (Wie bereits erwähnt, können funktionale Schnittstellen jeweils nur eine Methode haben.)
Einige weitere Beispiele: Die
accept
Methode in Consumer nimmt eine Eingabe entgegen, gibt jedoch nichts zurück.Oben
getRandom
nimmt kein Argument und gibt a zurückdouble
. Daher kann jede funktionale Schnittstelle verwendet werden, die die Kriterien erfüllt: kein Argument nehmen und zurückgebendouble
.Ein anderes Beispiel:
Bei parametrisierten Typen :
Methodenreferenzen können unterschiedliche Stile haben, aber im Grunde bedeuten sie alle dasselbe und können einfach als Lambdas visualisiert werden:
ClassName::methName
)instanceRef::methName
)super::methName
)ClassName::methName
)ClassName::new
)TypeName[]::new
)Weitere Informationen finden Sie unter http://cr.openjdk.java.net/~briangoetz/lambda/lambda-state-final.html .
quelle
Ja, das ist wahr. Der
::
Operator wird für die Methodenreferenzierung verwendet. Man kann also statische Methoden aus Klassen extrahieren , indem man sie verwendet, oder Methoden aus Objekten. Der gleiche Operator kann auch für Konstruktoren verwendet werden. Alle hier genannten Fälle sind im folgenden Codebeispiel beispielhaft aufgeführt.Die offizielle Dokumentation von Oracle finden Sie hier .
In diesem Artikel erhalten Sie einen besseren Überblick über die JDK 8-Änderungen . Im Abschnitt Referenzieren von Methoden / Konstruktoren wird auch ein Codebeispiel bereitgestellt:
quelle
method(Math::max);
ist Aufruf und Definition der Methode wäre wiepublic static void method(IntBinaryOperator op){System.out.println(op.applyAsInt(1, 2));}
. So wird es benutzt.Es scheint etwas spät zu sein, aber hier sind meine zwei Cent. Ein Lambda-Ausdruck wird verwendet, um anonyme Methoden zu erstellen. Es wird lediglich eine vorhandene Methode aufgerufen, es ist jedoch klarer, die Methode direkt anhand ihres Namens zu referenzieren. Und die Methodenreferenz ermöglicht es uns, dies mit dem Methodenreferenzoperator zu tun
::
.Betrachten Sie die folgende einfache Klasse, in der jeder Mitarbeiter einen Namen und eine Besoldungsgruppe hat.
Angenommen, wir haben eine Liste der Mitarbeiter, die nach einer bestimmten Methode zurückgegeben wurden, und wir möchten die Mitarbeiter nach ihrer Besoldungsgruppe sortieren. Wir wissen, dass wir anonyme Klassen verwenden können als:
Dabei ist getDummyEmployee () eine Methode wie:
Jetzt wissen wir, dass der Komparator eine funktionale Schnittstelle ist. Eine funktionale Schnittstelle ist die mit genau einer abstrakten Methode (obwohl sie eine oder mehrere Standard- oder statische Methoden enthalten kann). Der Lambda-Ausdruck bietet eine Implementierung,
@FunctionalInterface
sodass eine funktionale Schnittstelle nur eine abstrakte Methode haben kann. Wir können den Lambda-Ausdruck verwenden als:Es scheint alles gut zu sein, aber was ist, wenn die Klasse
Employee
auch eine ähnliche Methode bereitstellt:In diesem Fall ist die Verwendung des Methodennamens selbst klarer. Daher können wir uns direkt auf die Methode beziehen, indem wir die Methodenreferenz verwenden als:
Wie pro docs gibt vier Arten von Verfahren Referenzen sind:
quelle
::
ist ein neuer in Java 8 enthaltener Operator, mit dem auf eine Methode einer vorhandenen Klasse verwiesen wird. Sie können statische und nicht statische Methoden einer Klasse referenzieren.Für die Referenzierung statischer Methoden lautet die Syntax:
Für das Verweisen auf nicht statische Methoden lautet die Syntax
Und
Die einzige Voraussetzung für die Referenzierung einer Methode ist, dass die Methode in einer Funktionsschnittstelle vorhanden ist, die mit der Methodenreferenz kompatibel sein muss.
Wenn Methodenreferenzen ausgewertet werden, wird eine Instanz der Funktionsschnittstelle erstellt.
Gefunden unter: http://www.speakingcs.com/2014/08/method-references-in-java-8.html
quelle
Dies ist eine Methodenreferenz in Java 8. Die Oracle-Dokumentation finden Sie hier .
Wie in der Dokumentation angegeben ...
quelle
:: Der Operator wurde in Java 8 für Methodenreferenzen eingeführt. Eine Methodenreferenz ist die Kurzsyntax für einen Lambda-Ausdruck, der nur EINE Methode ausführt. Hier ist die allgemeine Syntax einer Methodenreferenz:
Wir wissen, dass wir Lambda-Ausdrücke anstelle einer anonymen Klasse verwenden können. Aber manchmal ist der Lambda-Ausdruck wirklich nur ein Aufruf einer Methode, zum Beispiel:
Um den Code klarer zu gestalten, können Sie diesen Lambda-Ausdruck in eine Methodenreferenz umwandeln:
quelle
Das :: wird als Methodenreferenz bezeichnet. Nehmen wir an, wir möchten eine berechnePreismethode der Klasse Purchase aufrufen. Dann können wir es schreiben als:
Es kann auch als Kurzform zum Schreiben des Lambda-Ausdrucks angesehen werden, da Methodenreferenzen in Lambda-Ausdrücke konvertiert werden.
quelle
Ich fand diese Quelle sehr interessant.
Tatsächlich ist es das Lambda , das sich in einen Doppelpunkt verwandelt . Der Doppelpunkt ist besser lesbar. Wir folgen diesen Schritten:
SCHRITT 1:
SCHRITT 2:
SCHRITT 3:
quelle
Person::getAge()
sollte seinPerson::getAge
.return reduce(Math::max);
ist NICHT GLEICH zureturn reduce(max());
Aber es bedeutet so etwas:
Sie können nur 47 Tastenanschläge speichern, wenn Sie so schreiben
quelle
Da hier viele Antworten das
::
Verhalten gut erklärten , möchte ich zusätzlich klarstellen, dass der::
Operator nicht genau dieselbe Signatur wie die verweisende Funktionsschnittstelle haben muss, wenn er beispielsweise für Variablen verwendet wird . Nehmen wir an, wir brauchen einen BinaryOperator mit dem Typ TestObject . Auf traditionelle Weise wird es folgendermaßen implementiert:Wie Sie in der anonymen Implementierung sehen, sind zwei TestObject-Argumente erforderlich, und es wird auch ein TestObject-Objekt zurückgegeben. Um diese Bedingung mit dem
::
Operator zu erfüllen, können wir mit einer statischen Methode beginnen:und dann anrufen:
Ok, es wurde gut kompiliert. Was ist, wenn wir eine Instanzmethode benötigen? Aktualisieren Sie TestObject mit der Instanzmethode:
Jetzt können wir wie folgt auf die Instanz zugreifen:
Dieser Code wird gut kompiliert, aber unter einem nicht:
Meine Sonnenfinsternis sagt mir: "Es kann kein statischer Verweis auf die nicht statische Methode testInstance (TestObject, TestObject) vom Typ TestObject ... erstellt werden."
Fair genug, es ist eine Instanzmethode, aber wenn wir
testInstance
wie folgt überladen :Und Ruf an:
Der Code wird einfach gut kompiliert. Weil es
testInstance
mit einem einzelnen Parameter anstelle eines doppelten aufgerufen wird . Ok, was ist mit unseren beiden Parametern passiert? Lass uns ausdrucken und sehen:Welches wird ausgegeben:
Ok, JVM ist intelligent genug, um param1.testInstance (param2) aufzurufen. Können wir
testInstance
von einer anderen Ressource verwenden, aber nicht von TestObject, dh:Und Ruf an:
Es wird einfach nicht kompiliert und der Compiler sagt: "Der Typ TestUtil definiert nicht testInstance (TestObject, TestObject)" . Der Compiler sucht also nach einer statischen Referenz, wenn es sich nicht um denselben Typ handelt. Ok, was ist mit Polymorphismus? Wenn wir endgültige Modifikatoren entfernen und unsere SubTestObject- Klasse hinzufügen :
Und Ruf an:
Es wird auch nicht kompiliert, der Compiler sucht weiterhin nach statischen Referenzen. Aber der folgende Code wird gut kompiliert, da er einen Test besteht:
quelle
In Java-8 Streams Reducer ist in einfachen Arbeiten eine Funktion, die zwei Werte als Eingabe verwendet und nach einiger Berechnung das Ergebnis zurückgibt. Dieses Ergebnis wird in der nächsten Iteration eingespeist.
Im Fall der Funktion Math: max gibt die Methode immer wieder maximal zwei übergebene Werte zurück, und am Ende haben Sie die größte Anzahl zur Hand.
quelle
Zur Laufzeit verhalten sie sich genau gleich. Der Bytecode kann / kann nicht gleich sein.
Zur Laufzeit verhalten sie sich genau gleich. Method (math :: max); generiert die gleiche Mathematik (siehe oben und überprüfe javap -c;))
quelle
In älteren Java-Versionen können Sie anstelle von "::" oder lambd Folgendes verwenden:
Oder zur Methode übergehen:
quelle
So sehe ich hier jede Menge Antworten , die offen zu kompliziert sind, und das ist eine Untertreibung.
Die Antwort ist ziemlich einfach: :: Es wird als Methodenreferenz bezeichnet https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html
Damit ich nicht kopiere und einfüge, können Sie auf dem Link alle Informationen finden, wenn Sie zur Tabelle scrollen.
Lassen Sie uns nun einen kurzen Blick auf die Methodenreferenzen werfen:
A :: B ersetzt etwas den folgenden Inline-Lambda-Ausdruck : (params ...) -> AB (params ...)
Um dies mit Ihren Fragen zu korrelieren, ist es notwendig, einen Java-Lambda-Ausdruck zu verstehen. Welches ist nicht schwer.
Ein Inline-Lambda-Ausdruck ähnelt einer definierten funktionalen Schnittstelle (eine Schnittstelle mit nicht mehr und nicht weniger als einer Methode) . Lassen Sie uns einen kurzen Blick darauf werfen, was ich meine:
InterfaceX muss eine funktionale Schnittstelle sein. Für jede funktionale Schnittstelle ist das einzige, was an InterfaceX für diesen Compiler wichtig ist, dass Sie das Format definieren:
InterfaceX kann eines davon sein:
oder dieses
oder allgemeiner:
Nehmen wir den ersten vorgestellten Fall und den zuvor definierten Inline-Lambda-Ausdruck.
Vor Java 8 hätten Sie es folgendermaßen definieren können:
Funktionell ist es das gleiche. Der Unterschied besteht eher darin, wie der Compiler dies wahrnimmt.
Nachdem wir uns den Inline-Lambda-Ausdruck angesehen haben, kehren wir zu den Methodenreferenzen (: :) zurück. Angenommen, Sie haben eine Klasse wie diese:
Da die Methode anyFunctions dieselben Typen wie InterfaceX callMe hat , können wir diese beiden mit einer Methodenreferenz gleichsetzen.
Wir können es so schreiben:
und das ist gleichbedeutend damit:
Eine coole Sache und ein Vorteil von Methodenreferenzen ist, dass sie zunächst typenlos sind, bis Sie sie Variablen zuweisen. Sie können sie also als Parameter an jede gleichwertig aussehende (mit denselben definierten Typen) Funktionsschnittstelle übergeben. Welches ist genau das, was in Ihrem Fall passiert
quelle
Die vorherigen Antworten sind ziemlich vollständig in Bezug auf was
::
Methodenreferenz. Zusammenfassend bietet es eine Möglichkeit, auf eine Methode (oder einen Konstruktor) zu verweisen, ohne sie auszuführen, und erstellt bei der Auswertung eine Instanz der Funktionsschnittstelle, die den Zieltypkontext bereitstellt.Im Folgenden finden Sie zwei Beispiele, um ein Objekt mit dem Maximalwert in einem
ArrayList
WITH und WITHOUT unter Verwendung der::
Methodenreferenz zu finden. Erklärungen finden Sie in den Kommentaren unten.OHNE die Verwendung von
::
Mit Hilfe von
::
quelle
Der Doppelpunkt, dh der
::
Operator, wird in Java 8 als Methodenreferenz eingeführt . Die Methodenreferenz ist eine Form des Lambda-Ausdrucks dem die vorhandene Methode anhand ihres Namens .Klassenname :: Methodenname
Ex:-
stream.forEach(element -> System.out.println(element))
Mit Double Colon
::
stream.forEach(System.out::println(element))
quelle