Der 2013 veröffentlichte Javase8 wird einen Plan namens Lambda Project enthalten, der im Juni dieses Jahres im JSR-335-Entwurf beschrieben wird.
JSR-335 führte den Verschluss in Java ein. Der Verschluss existiert in vielen beliebten Sprachen, wie C ++, C#. Der Verschluss ermöglicht es uns, einen Funktionszeiger zu erstellen und sie als Parameter zu übergeben. In diesem Artikel werden wir uns grob die Eigenschaften von Java8 ansehen und Lambda -Ausdrücke vorstellen. Und ich werde versuchen, einige Beispielprogramme zu platzieren, um einige Konzepte und Grammatik zu erklären.
Die Java -Programmiersprache bietet uns das Konzept der Schnittstelle, und die abstrakte Methode kann in der Schnittstelle definiert werden. Die Schnittstelle definiert die API und hofft, dass Benutzer oder Lieferanten diese Methoden implementieren. Oft erstellen wir keine unabhängigen Implementierungsklassen für einige Schnittstellen.
Die anonyme Verwendung wird häufig verwendet. Die häufigste Szene, die in der anonymen internen Klasse verwendet wird, ist Ereignisverarbeitungsprozessor. Zweitens werden anonyme interne Klassen häufig in Multi -Thread -Programmen verwendet.
Genau wie wir diskutieren, ist eine anonyme Klasse die Implementierung einer bestimmten Schnittstelle eines Ninja. Normalerweise übergeben wir das Objekt dieser Implementierungsklasse als Parameter an eine Methode, und dann ruft diese Methode die Implementierungsklassenmethode intern auf die Implementierungsklasse auf. Daher wird diese Schnittstelle als Rückrufschnittstelle bezeichnet.
Obwohl überall anonyme Kategorien verwendet werden, haben sie immer noch viele Probleme. Das erste Hauptproblem ist die Komplexität. Diese Klassen lassen die Ebenen des Codes chaotisch und kompliziert aussehen, auch als vertikales Privem bekannt. Zweitens können sie nicht auf die Nicht -Final -Mitglieder der Verpackungsklasse zugreifen. Das Schlüsselwort davon wird sehr verwirrend. Wenn eine anonyme Klasse den gleichen Mitgliedsnamen wie ihre Verpackungsklasse hat, werden die internen Variablen die externen Mitgliedsvariablen abdecken. Weil dieses Schlüsselwort eher anonymer Objekte selbst als seinem Kapselungsobjekt verdient.
Public void anonymousexample () {String NonfinalVariable = "Nonformal Exmple"; METHODE Variable "; // unterhalb der Zeile gibt Kompilierungsfehler. println ("->" + this.Variable);}}). Die Ausgabe ist:
-> Methode Varial-> Runnable-Klassenmitglied ausführen
In diesem Beispiel wird das oben erwähnte Problem erklärt, und der Lambda -Ausdruck löst fast alle Probleme, die durch anonyme interne Klassen verursacht werden. Bevor wir den Lambda -Ausdruck weiter untersuchen, schauen wir uns die funktionalen Schnittstellen an.
Funktionale Schnittstellen
Funktionale Schnittstellen sind eine Schnittstelle mit nur einer einzelnen Methode, die diesen Methodenvertrag darstellt.
Nur einer in der obigen Definition ist nicht so einfach. Ich verstehe diesen Absatz nicht.
Das folgende Beispiel zeigt deutlich, wie das Konzept der funktionalen Schnittstellen versteht.
Schnittstelle Runnable {void run ();} // FunctionalInterface foo {boolean Equals (Objekt obj);} // nicht funktional; Funktional; nicht funktional; Gleiche SignaturDie meisten Rückrufschnittstellen sind funktionale Schnittstellen. Zum Beispiel runnable, Callable, Comparator usw. Es wurde zuvor als SAM bezeichnet (Single Abstract -Methode)
Lambda -Ausdruck
Wie gesagt, ein Hauptproblem der anonymen Kategorie ist, dass die Ebene des Codes chaotisch aussieht, das heißt, die vertikale Privente. Der Lambda -Ausdruck sieht aus wie eine Methode. Sie haben eine formale Parameterliste und Blockierung dieser Parameter.
(String S) -> s.lengh; () -> 43;
Das obige Beispiel bedeutet, dass der erste Ausdruck eine Zeichenfolgenvariable als Parameter empfängt und dann die Länge der Zeichenfolge zurückgibt. Die zweite ohne Parameter und zurückgegeben 43. Schließlich akzeptierte der dritte zwei Ganzzahl X und Y und kehrte zum Frieden zurück.
Nachdem ich viele Wörter gelesen habe, kann ich schließlich ein Beispiel für den ersten Lambda -Ausdruck geben.
Öffentliche Klasse FirstlambdaExpression {public String variable = "Class Level Variable"; String NonfinalVariable = "Dies ist nicht endgültige Variable"; ; System.out.println ("->" "" + this.Variable); Die Ausgabe ist:
-> Methode Lokale Variable-> Klassenebenevariable
Sie können den Unterschied zwischen der Verwendung von Lambda -Ausdrücken und der anonymen internen Klasse vergleichen. Wir können eindeutig sagen, dass das Schreiben anonymer Kategorien mit dem Lambda -Ausdruck das Problem der variablen Sichtbarkeit löst. Sie können sich die Anmerkungen im Code ansehen.
Die allgemeine Lambda-Expressionssyntax enthält eine Parameterliste, das Arrow-Schlüsselwort "->" ist der Hauptkörper. Der Subjekt kann ein Ausdruck (eine Anweisung -Zeile) oder ein Multi -Line -Satz sein. Wenn es sich um einen Ausdruck handelt, wird es berechnet und zurückgegeben. Brechen und fortfahren können nur im Zyklus verwendet werden.
Warum diese spezielle Grammatikform auswählen, denn derzeit befindet sich dieser Stil normalerweise in C# und Scala, was auch ein allgemeines Schreiben des Lambda -Ausdrucks ist. Dieses Grammatikdesign löst im Grunde die Komplexität des anonymen Typs. Gleichzeitig ist er auch sehr flexibel. Das Ergebnis des Ausdrucks ist sein eigener Rückgabewert. Diese Flexibilität kann den Code einfach halten.
Der Lambda -Ausdruck wird als anonym verwendet, sodass sie flexibel in anderen Modulen oder anderen Lambda -Ausdrücken (verschachtelte Lambda -Ausdrücke) verwendet werden können.
// Lambda Exposition ist mit Methoden der Parameterblockade eingeschlossen. (() -> {System.out.println ("in unterschiedlichem Thread laufen");});
Wenn Sie sich den Lambda -Ausdruck genauer ansehen, werden Sie feststellen, dass der Zielschnitteltyp nicht Teil eines Ausdrucks ist. Der Compiler hilft dabei, die Art und die umgebende Umgebung des Lambda -Ausdrucks zu schließen.
Der Lambda -Ausdruck muss einen Zieltyp haben und sie können sich an einen möglichen Zieltyp anpassen. Wenn der Zieltyp eine Schnittstelle ist, müssen die folgenden Bedingungen erfüllt sein, um korrekt zu kompilieren:
Da der Compiler den Parametertyp und die Zahl über die Anweisung des Zieltyps kennen, kann die Deklaration von Parametertypen im Lambda -Ausdruck weggelassen werden.
Vergleicher C = (S1, S2) -> S1.comParetoignoreCase (S2);
Wenn die im Zieltyp deklarierte Methode nur nur einen Parameter empfängt (oft dies ist der Fall), können die kleinen Klammern des Parameters auch geschrieben werden, zum Beispiel:
ActionListenr listenr = event-> event.getwhen ();
Eine sehr offensichtliche Frage kommt: Warum drückt Lambda keinen bestimmten Methodennamen aus?
Die Antwort lautet: Der Lambda -Ausdruck kann nur für die funktionale Schnittstelle verwendet werden, während die funktionale Schnittstelle nur eine Methode hat.
Wenn wir eine funktionale Schnittstelle zum Erstellen von Lambda -Ausdrücken bestimmen, kann der Compiler die Signatur des chinesischen Gesetzes der funktionalen Schnittstelle wahrnehmen und überprüfen, ob der angegebene Ausdruck übereinstimmt.
Diese flexible Grammatik hilft uns zu vermeiden, anonymer vertikales Privem zu verwenden, und bringt kein horizontales Privem (sehr langer Satz).
Die Lambda -Expression -Grammatik hängt mit dem Kontext zusammen, aber dies ist nicht das erste Mal. Von Java SE 7 hinzugefügte Diamantbetreiber hat auch dieses Konzept, das durch den Kontext abgeleitet wird.
void invoke (runnable r) {r.run ()} void Future Invoke (Callable R) {return c.compute ()} // oben sind zwei Methoden Onal InterfaceFuture S = invoke () -> "Done"); / Welcher Aufruf wird angerufen?
Die Antwort auf die obige Frage lautet, die Methode zum Empfangen des Callable Parameters aufzurufen. In diesem Fall wird der Compiler durch die Last verschiedener Parametertypen aufgelöst. Wenn mehr als eine anwendbare Lademethode vorliegt, überprüft der Compiler auch die Kompatibilität des Lambda -Expression und den entsprechenden Zieltyp. Einfach ausgedrückt wird die obige Invoke -Methode zurückgegeben, aber nur eine Invoke -Methode hat einen Rückgabewert.
Lambda -Ausdrücke können explizit in bestimmte Zieltypen umgewandelt werden, solange sie mit entsprechenden Typen kompatibel sind. Als ich mir das folgende Programm ansah, habe ich drei Arten von Callable implementiert und sie alle in einen Klassentyp umgewandelt.
Öffentliche Klasse FirstWithlambdaExpressions {public static void main (String [] args) {listl = arrayList (Callial)-> "Callable 1", (Call fähig) ()-> "Callable 2", (Callable) (Callable))-> "Callable 3"); ) {e1.printstacktrace ();} e.Shutdown ();} public void Dumplist (Listliste) löst Interuptexception, Executexcepti auf {für (Zukunft: Liste) {System.out.println (Future.get ()) aus. }}}Wie wir bereits erläutert haben, können anonyme Kategorien nicht auf die Nicht -Final -Variablen in der Umgebung zugreifen. Aber es gibt keine solche Grenze im Lambda -Ausdruck.
Derzeit sind die definierten funktionalen Schnittstellen nur für die Schnittstelle anwendbar. Ich habe versucht, einen Lambda -Expression von nur einer abstrakten Methode zu erstellen, aber ein Kompilierungsfehler wurde gemacht. Laut JSR -335 kann die zukünftige Version des Lambda -Ausdrucks Funktionsklassen unterstützen.
Methodenzitat
Methoden, die als Referenzmethode verwiesen werden, ohne sie aufzurufen.
Mit dem Lambda -Ausdruck können wir eine anonyme Methode definieren und als Instanz der funktionalen Schnittstelle verwenden. Methoden sind dem Lambda -Ausdruck sehr ähnlich.
System :: GetProperty "ABC" :: PlayString :: Spezifikum :: ToStringArrayList :: New
Die obige Anweisung zeigt die allgemeine Syntax der Methode und den Verweis auf den Konstruktor. Hier haben wir einen neuen operativen Charakter gesehen ":::: Doppelkolon. Als Referenz werden wir es im Bereich dieses Tutorials einfach als Separatist verwenden.
Die Zielreferenz oder der Empfänger befindet sich hinter dem Anbieter und den Separatoren. Dies bildet einen Ausdruck, der eine Methode zitieren kann. In der endgültigen Erklärung ist der Name der Methode "neu". Dieser Ausdruck zitiert die Strukturmethode der ArrayList -Klasse (die Referenz auf den Konstruktor im nächsten Abschnitt)
Bevor Sie dies verstehen, möchte ich die Stärke der zitierten Methode sehen.
Java.util.Arrays; = {Neuer Mitarbeiter ("Nick"), neuer Mitarbeiter ("Robin"), neuer Mitarbeiter ("Josh"), neuer Mitarbeiter ("Andy"), neuer Mitarbeiter ("Mark")}; Vor der Sortierung: "); DumpoPoPoToMeee (Mitarbeiter); Arrays.sort (Mitarbeiter, Mitarbeiter :: MyCompare); System.out.println (" After Sort: "); ASLIST (Mitarbeiter) {System.out.print (emp.name+",");} System.out.println (); (Mitarbeiter EMP1, Mitarbeiter EMP2) {return emp1.name.comPareto (EMP2.Name);}} Die Ausgabe ist:
Vor der Sortierung: Nick, Robin, Josh, Andy, Mark, After Sort: Andy, Josh, Mark, Nick, Robin,
Die Ausgabe ist nicht etwas Besonderes. Statische Methode MyCompare erhält zwei Mitarbeiterobjekte und gibt ihre Namen zum Vergleichen zurück.
In der Hauptmethode habe ich ein anderes Array eines Mitarbeiter erstellt und an die Arrays.sort -Methode weitergegeben, um sie auf das Ausdrucksarray zu verweisen (Mitarbeiter :: MyCompare).
Warten Sie eine Minute, wenn wir uns Javadoc ansehen, werden Sie feststellen, dass der zweite Parameter der Sortiermethode der Koraratortyp ist, aber wir übergeben die statische Methodenreferenz des Mitarbeiters. Das wichtige Problem ist hier.
Schauen wir uns an, warum. Die Arrays.sort -Methode erwartet eine Instanz eines Komparators, und dieser Komparator ist eine funktionale Schnittstelle, was bedeutet, dass er nur eine Methode hat, dh vergleichen. Hier geben wir auch einen Lambda -Ausdruck böswillig weiter, der die Implementierung der Compaare -Methode in diesem Ausdruck liefert. Aber in uns hat unsere Mitarbeiterklasse bereits eine Vergleichsmethode. Es ist nur so, dass ihre Namen unterschiedlich sind.
Wenn es mehrere gleichnamige Methoden gibt, wählt der Compiler die beste Übereinstimmung entsprechend dem Zieltyp. Um zu verstehen, schauen Sie sich ein Beispiel an:
Public static int mycompare (Mitarbeiter EMP1, Mitarbeiter EMP2) {return emp1.name.comPareto (EMP2.Name);} // Eine andere Methode mit demselben Namen wie der obigen. {{) {{) {{Return int1.comPareto (int2);} Ich habe zwei verschiedene Arrays zum Sortieren erstellt.
Mitarbeiter [] Mitarbeiter = {New Employee ("Nick"), neuer Mitarbeiter ("Robin"), neuer Mitarbeiter ("Josh"), neuer Mitarbeiter ("Andy"), neuer Mitarbeiter ("Mark")}; Ins = {1, 4, 8, 2, 3, 8, 6}; Jetzt führe ich die folgenden zwei Codezeilen aus
Arrays.sort (Mitarbeiter, Mitarbeiter :: MyCompare);
Hier ist die Methode der Referenzen in den beiden Codezeilen gleich (Mitarbeiter :: MyCompare).
Lassen Sie sich nicht von der statischen Methode irregeführt. Wir können auch einen Verweis auf die Beispielmethode erstellen. Für statische Methoden verwenden wir Klassennamen :: Method -Name, um Methodenreferenz zu schreiben.
Das obige Beispiel ist ziemlich gut, aber wir müssen keine Methode zum Vergleich der Ganzzahl schreiben, da Integer vergleichbar implementiert und eine Implementierungsmethode vergleichbar ist. Wir verwenden also nur die folgende Zeile direkt:
Arrays.sort (ints, Integer :: Compareto);
Fühlen Sie sich ein wenig verwirrt? NEIN? Dann lassen Sie mich hier verwirren. Die Mitgliedermethode wird referenziert :: Es sollte ein Objekt vorher sein, aber warum der Satz hier tatsächlich legitim ist.
Die Antwort lautet: Diese Art der Anweisung ermöglicht die Verwendung in bestimmten Typen. Integer ist ein Datentyp, und für den Datentyp ist diese Anweisung zulässig.
Wenn wir die Mitarbeitermethode mycompare in nicht -statische verwandeln und dann verwenden: Mitarbeiter :: MyCompare, gibt es Kompilierungsfehler: Keine geeignete Methode gefunden.
Referenz für konstruktive Methode
Die Konstruktorreferenzen werden als Klasse verwendet, die auf einen Konstruktor ohne angegebene Institutionalisierung verweist.
Die konstruktive Methodereferenz ist ein neues Merkmal von Javase 8. Wir können einen Verweis auf eine konstruktive Methode erstellen und als Parameter an den Zieltyp übergeben.
Wenn wir es zur Referenz verwenden, zitieren wir eine vorhandene Methode, um sie zu verwenden. In ähnlicher Weise erstellen wir bei Verwendung der konstruktiven Methodenreferenz eine Referenz auf vorhandene konstruktive Methoden.
Im vorherigen Abschnitt haben wir den vom Konstruktor verwiesenen Grammatiknamen gesehen :: New, der wie eine Methodenreferenz aussieht. Der Verweis auf diese konstruierte Methode kann den Instanzen der Zielfunktionsschnittstellen zugewiesen werden. In einer Klasse kann es mehrere Konstruktoren geben.
Für mich ist es schwierig, die erste konstruktive Methode zu schreiben. Am Ende habe ich lange hart gearbeitet und schließlich "ah, ich fand ...", schaute mir das folgende Verfahren an.
Public class constructorreferences {public static void main (String [] ar) {myInterface in = myclass :: new; } Die Ausgabe ist:
-> com.myclass@34e5307e
Dies sieht ein bisschen erstaunlich aus, oder?
In diesem Beispiel wurde ein weiteres Problem in meinem Herzen hervorgerufen: Wie kann eine konstruktive Methode mit einem Parameter instanziiert? Schauen Sie sich das folgende Verfahren an:
Public class constructorreferences {public static void main (String [] ar) {EmlpoyeProder Provider = Mitarbeiter :: New; ; Alter) {this.name = name; Die Ausgabe ist:
-> Mitarbeitername: John-> Mitarbeiter Alter: 30
Schauen wir uns vor dem Lesen dieses Artikels die coolste Funktion in der Javase8-Default-Methode an
Standardmethoden
Javase8 führt ein Konzept ein, das als Standardmethode bezeichnet wird. Die frühe Java -Version der Schnittstelle hat eine sehr strenge Schnittstelle. In der kommenden Java -Version ist die Standardimplementierung der Methode in der Schnittstelle zulässig. Nicht viel Unsinn, schauen Sie sich Folgendes an:
Public class defaultMethods {public static void main (String [] ar) {NormalInterface Instance = NewInterfaceImpl (); System.out.println ("-> MyDefaultMethod");}} Class NormalInterfaceIMPL IMPLEMELEMERINTERFACE {@Override Public Void MyNormalMethod () {) System.out.println ("-> MynormalMethod");}} Die Ausgabe ist:
-> mydefaultMethod
Die obige Schnittstelle deklariert zwei Methoden, aber die Implementierungsklasse dieser Schnittstelle realisiert nur eine davon, da MyDefaultMethod die Standardmodifikatoren verwendet und einen Methodenblock für die Standardimplementierung bietet. Die schweren Lastregeln von GM werden hier immer noch wirksam. Wenn die Implementierungsklasse die Methode in der Schnittstelle implementiert, ist dies die Methode in der Anrufklasse beim Aufrufen. Andernfalls wird die Standardimplementierung aufgerufen.
Die Schnittstelle der integrierten übergeordneten Schnittstelle kann die Standardimplementierung der übergeordneten Schnittstelle erhöhen, ändern und entfernen.
Schnittstelle übergeordnetes {void anfangsnomal (); -> anfangsnomal ");} void anfangsdefault (); // jetzt eine normale Methode} In diesem Beispiel definiert ParentInterface zwei Methoden, und der andere wird standardmäßig implementiert.
Stellen Sie sich eine Klasse vor, die die Klasse C erbte, die Schnittstelle I erkannte, und C hatte eine Methode, und die Methode, die die Standardmethode bereitstellte, bei der die Standardmethode bereitgestellt wurde, war kompatibel. In diesem Fall hat die Methode in C der Standardmethode in I Vorrang, und selbst die Methode in C hat immer noch Priorität, wenn die Methode abstrakt ist.
Public class defaultMethods {public static void main (String [] ar) {interfaxe impl = new NormalInterfaceImpl (); }} interfaxe interfaxe {public void defaultMethod () Standard {System.out.println ("-> interfaxe"); Die Ausgabe ist:
-> Elternklasse
Das zweite Beispiel ist, dass meine Klasse zwei verschiedene Schnittstellen implementiert hat, aber beide Schnittstellen geben dieselbe Anweisung mit derselben Standard -Implementierungsmethode an. In diesem Fall kann der Compiler nicht herausfinden, was los ist. Dies kann auf folgende Arten erfolgen.
Public class defaultMethods {public static void main (String [] ar) {firstInterface Impl = new NormalInterfampl (); . Die Ausgabe ist:
-> Secondinterface
Jetzt haben wir die Einführung des Java -Verschlusses gelesen. In diesem Artikel kamen wir mit funktionalen Schnittstellen und Java -Verschluss in Kontakt, die Javas Lambda -Ausdruck, Methodenreferenzen und Konstruktorreferenzen verstanden haben. Und wir haben auch ein Hello World -Beispiel für Lambda -Ausdruck geschrieben.
Javase8 kommt bald.