Warum Lambda -Ausdrücke verwenden?
Schauen wir uns einige Beispiele an:
Das erste Beispiel ist die Ausführung einer Aufgabe in einem separaten Thread, den wir normalerweise wie folgt implementieren:
Klassenarbeiter implementiert runnable {public void run () {für (int i = 0; i <100; i ++) dowork (); } ...} Arbeiter w = neuer Arbeiter (); neuer Thread (w) .Start ();Das zweite Beispiel ist eine benutzerdefinierte String -Vergleichsmethode (nach Stringlänge), die im Allgemeinen durchgeführt wird:
Class Länge Comparator implementiert Compue <String> {public int compare (String zuerst, String zweiter) {return integer.comPare (first.length (), zweit.length ()); }} Arrays.sort (Zeichenfolgen, neuer LängeCompparator ());Fügen Sie im dritten Beispiel in Javafx einen Rückruf zu einer Taste hinzu:
button.setonaction (neuer EventHandler <ActionEvent> () {public void Handle (actionEvent event) {System.out.println ("Danke für das Klicken!");}});Diese Beispiele haben eines gemeinsam, dass sie zuerst einen Codeblock definieren, an ein Objekt oder eine Methode weitergeben und dann ausführen. Vor Lambda-Ausdrücken erlaubt Java keine direkte Übergabe von Codeblöcken, da Java objektorientiert ist. Daher muss ein Objekt übergeben werden, um den Codeblock in das Objekt zu verkapulieren.
Lambda Expressionsyntax
Der Längenvergleich im zweiten Beispiel wird als Lambda -Ausdruck ausgedrückt:
(String First, String Second) -> Integer.comPare (first.length (), Second.length ());
-> Vorher ist die Parameterliste, gefolgt von der Expressionsanweisung -Karosserie;
Wenn der Körper einer Expressionsanweisung mehr als eine Zeile ist, ist der Körper der Aussage in {} geschrieben, genau wie eine gewöhnliche Funktion:
(String zuerst, String Second) -> {if (first.Length ()> zweit.length ()) {return 1; } else if (first.length () == zweiten.length ()) {return 0; } else {return -1; }};Wenn es keine Parameter gibt, muss () noch mitgebracht werden. Zum Beispiel kann das erste Beispiel oben ausgedrückt werden als:
() -> {für (int i = 0; i <1000; i ++) {dowork (); }}Wenn der Typ des Parameters automatisch aus dem Kontext abgeleitet werden kann, können Sie weglassen:
Vergleiche <string> comp = (zuerst, zweitens) // gleich wie (String zuerst, String Second) -> Integer.comPare (first.Length (), Second.length ());
Wenn es nur einen Parameter gibt und der Typ automatisch abgeleitet werden kann, können auch die Klammern () weggelassen werden:
// anstelle von (Ereignis) -> oder (ActionEvent Event) -> EventHandler <ActionEvent> listener = Ereignis -> System.out.println ("Danke für das Klicken!");Die Art des Rückgabewerts des Lambda -Ausdrucks wird automatisch abgeleitet, sodass er nicht angegeben werden muss. Im Lambda -Ausdruck haben einige bedingte Zweige Rückgabewerte, andere Zweige haben keine Rückgabeteile, was nicht zulässig ist, wie z. B.:
(x) -> {if (x> = 0) {return 1; }}Darüber hinaus besteht der Unterschied zwischen Expression Lambda und Anweisung Lambda darin, dass Expression Lambda das Rückgabe -Keyword nicht schreiben muss. Die Java -Laufzeit gibt das Ergebnis des Ausdrucks als Rückgabewert zurück, während Anweisung Lambda ein in {} geschriebener Ausdruck ist, und das Rückgabe -Schlüsselwort muss zum Beispiel verwendet werden:
// Expression Lambdacparator <string> comp1 = (zuerst, zweite) -> Integer.comPare (first.length (), Second.length ()); // Anweisung Lambdacparator <string> comp2 = (zuerst, zweiten) -> {return Integer.compare (first.Length (), zweitens. Funktionsschnittstelle
Wenn eine Schnittstelle nur eine abstrakte Methode hat, heißt sie
Funktionale Schnittstelle wie Runnable, Comparator usw.
An jedem Ort, an dem funktionales Schnittstellenobjekt benötigt wird, können Sie Lambda -Ausdrücke verwenden:
Arrays.sort (wörter, (zuerst, zweite) -> integer.compare (first.length (), zweit.length ()));
Hier erfordert der zweite Parameter von Sort () ein Komparatorobjekt, und der Komparator ist
Funktionale Schnittstelle, sodass Sie den Lambda -Ausdruck direkt übergeben können. Beim Aufrufen der Vergleich () -Methode des Objekts soll der Anweisungskörper im Lambda -Ausdruck ausgeführt werden.
Wenn die Aussage des Lambda -Ausdrucks eine Ausnahme ausgelöst hat, muss die entsprechende abstrakte Methode in der funktionalen Schnittstelle die Ausnahme auswerfen. Andernfalls ist es erforderlich, die Ausnahme im Lambda -Ausdruck explizit zu fangen:
Runnable r = ()-> {System.out.println ("-------"); try {thread.sleep (10); } catch (InterruptedException e) {// Catch-Ausnahme}}; Callable <string> c = ()-> {System.out.println ("----------"); Thread.sleep (10); zurückkehren "";}; Methodenreferenz
Wenn die Parameter des Lambda -Ausdrucks als Parameter an eine Methode übergeben werden und deren Ausführungseffekt gleich ist, kann der Lambda -Ausdruck mithilfe der Methodenreferenz ausgedrückt werden und die folgenden zwei Methoden sind äquivalent:
(x) -> system.out.println (x) system.out :: println
Unter ihnen wird System.out :: println als Methodenreferenz bezeichnet.
Methodenreferenz ist hauptsächlich in drei Formen erhältlich:
Für die ersten beiden Methoden sind die entsprechenden Lambda -Expressionsparameter und Methodenparameter gleich, wie beispielsweise:
System.out :: println (x) -> system.out.println (x) math :: pow (x, y) -> math.pow (x, y)
Für die dritte Methode wird in der entsprechenden Lambda Expressions -Anweisungskörper der erste Parameter als Objekt verwendet, die Methode aufgerufen und andere Parameter als Methodenparameter verwendet, wie z. B.:
String :: ComparetoignoreCase (S1, S2) -> S1.comParetoignoreCase (S2) 1.5 Konstruktorreferenz
Die Konstruktorreferenz ähnelt der Methodenreferenz, ist jedoch eine spezielle Methode: neu. Der spezifische Konstruktor wird durch die Kontextumgebung bestimmt, wie z. B.:
List <String> Labels = ...; Stream <taste> stream = labels.stream (). Map (button :: new);
Button :: Neu ist äquivalent zu (x) -> Taste (x), so dass der aufgerufene Konstruktor: Taste (x);
Neben dem Erstellen eines einzelnen Objekts können Sie auch ein Array von Objekten erstellen, z. B. die folgenden zwei Äquivalente:
int [] :: new (x) -> new int [x]
Variabler Umfang
Lambd Expressions erfassen Variablen, die im aktuellen Bereich verfügbar sind, wie::
public void repepMessage (String text, int count) {runnable r = () -> {für (int i = 0; i <count; i ++) {System.out.println (text); Thread.yield (); }}; neuer Thread (r) .Start ();}Aber diese Variablen müssen unveränderlich sein, warum? Siehe das folgende Beispiel:
int mines = 0; für (Pfad P: Dateien) neuer Thread (() -> {if (p hat eine Eigenschaft) übereinstimmt ++;}). start (); // illegal zu mutieren ÜbereinstimmungenDa veränderliche Variablen in Lambda-Ausdrücken nicht mit Thread-sicher sind, stimmt dies mit den Anforderungen der inneren Klassen überein, und nur extern definierte endgültige Variablen können in inneren Klassen verwiesen werden.
Der Umfang des Lambda -Ausdrucks entspricht dem des verschachtelten Codeblocks, so
Path first = paths.get ("/usr/bin"); vergleicher <string> comp = (zuerst, zweite) -> integer.compare (first.length (), zweite.length ()); // Fehler: Variable zuerst bereits definiertWenn diese Variable in einem Lambda -Ausdruck referenziert wird, ist die Referenz diese Variable der Methode, die den Lambda -Ausdruck erstellt, wie z. B.:
public class application () {public void dowork () {runnable runner = () -> {...; System.out.println (this.toString ()); ...}; }} Also hier that this.toString () ruft toString () des Anwendungsobjekts auf, nicht ausgeführt
Objekt.
Standardmethode
Es kann nur abstrakte Methoden in der Schnittstelle geben. Wenn einer vorhandenen Schnittstelle eine neue Methode hinzugefügt wird, müssen alle Implementierungsklassen der Schnittstelle diese Methode implementieren.
Java 8 führt das Konzept der Standardmethode ein und fügt der Schnittstelle eine Standardmethode hinzu, die die vorhandenen Grenzflächenregeln nicht zerstört. Die Schnittstellen -Implementierungsklasse kann die Standardmethode überschreiben oder direkt erben, z. B.:
Schnittstellenperson {long getId (); Standard -String getName () {return "John Q. public"; }}Java ermöglicht mehrere Vererbung. Wie kann man mit diesem Konflikt umgehen, wenn die in der übergeordneten Klasse einer Klasse definierten Methoden genau den Standardmethoden entsprechen, die in der Schnittstelle definiert sind, oder die beiden Schnittstellen einer Klasse genau gleich. Wie geht es mit diesem Konflikt um? Die Verarbeitungsregeln sind wie folgt:
Wenn die Methode in Konflikt zwischen der übergeordneten Klasse und der Schnittstelle in Konflikt steht: Die Methoden in der übergeordneten Klasse haben sich durch, und die Methoden in der Schnittstelle müssen ignoriert werden;
Wenn die Standardmethode in den beiden Schnittstellenkonflikten Konflikte, müssen Sie die Methode überschreiben, um den Konflikt zu lösen.
Statische Methode
Vor Java 8 können nur statische Variablen in der Schnittstelle definiert werden. Ausgehend von Java 8 können statische Methoden zur Schnittstelle hinzugefügt werden, wie z.
Die Komparatorschnittstelle hat eine Reihe statischer Vergleichsmethoden hinzugefügt, wie z. B.:
public static <T> vergleicher <t> Vergleiche (tointfunction <? Super t> keyExtractor) {Objects.requirenonnull (KeyExtractor); return (vergleicher <t> & serialisierbar) (C1, C2) -> Integer.comPare (KeyExtractor.Applyaint (C1), KeyExtractor.Applyasint (C2));}Mit dieser statischen Methode sind auch die folgenden zwei Methoden gleichwertig:
1.
Arrays.sort (Städte, (zuerst, zweite) -> Integer.comPare (first.length (), Second.length ()));
2.
Arrays.sort (Städte, vergleicher.comParingint (String :: Länge));
Wenn wir in Zukunft unsere eigenen Schnittstellen entwerfen, müssen wir daher keine separaten Werkzeugklassen mehr definieren (z. B. Sammlungen/Sammlung).
Verwenden Sie einfach die statische Methode in der Schnittstelle.
Anonyme interne Klasse
In der Java -Welt können anonyme innere Klassen Vorgänge implementieren, die möglicherweise nur einmal in einer Anwendung durchgeführt werden. In einer Android -Anwendung wird beispielsweise eine Schaltfläche Klickereignis behandelt. Sie müssen keine separate Klasse schreiben, um ein Klickereignis zu verarbeiten. Sie können dies mit einer anonymen inneren Klasse tun:
Taste button = (button) findViewById (r.id.button1); button.setonclickListener (neuer OnclickListener () {@Override public void onclick (Ansicht) {toast.maketext (MainActivity.This, "Taste klickte", Toast.Legth_Short) .Show). Lambda Beispiel 1. Runnable Lambda Schauen wir uns mehrere Beispiele an. Hier ist ein Beispiel für Runnable: public void runnabletest () {System.out.println ("=== Runnabletest ==="); // Anonymous Runnable Runnable r1 = new Runnable () {@Override public void run () {System.out.println ("Hello World One!"); }}; // lambda runnable runnable r2 = () -> system.out.println ("Hello World Two!"); // zwei ausgeführte Funktionen ausführen r1.run (); r2.run (); } public void runnabletest () {System.out.println ("=== Runnabletest ==="); // Anonymous Runnable Runnable r1 = new Runnable () {@Override public void run () {System.out.println ("Hello World One!"); }}; // lambda runnable runnable r2 = () -> system.out.println ("Hello World Two!"); // zwei ausgeführte Funktionen ausführen r1.run (); r2.run (); } Weder die Implementierung noch der Rückgabewert werden zurückgegeben. Runnable Lambda-Ausdrücke verwenden Codeblöcke, um den fünfstöckigen Code in einer Anweisung zu vereinfachen. public class Person {private Zeichenfolge gegebenen Namen; privater String -Nachname; privates int Alter; privates Geschlecht des Geschlechts; private Zeichenfolge E -Mail; privates String -Telefon; private String -Adresse;} public class Person {private Zeichenfolge gegebenen Namen; privater String -Nachname; privates int Alter; privates Geschlecht des Geschlechts; private Zeichenfolge E -Mail; privates String -Telefon; private String -Adresse;} Im Folgenden werden die Komparatorschnittstelle mithilfe anonymer innerer Klassen und Lambda -Ausdrücke implementiert: public class vergleichste {public static void main (String [] args) {list <Person> personList = person.createShortList (); // Die innere Klasse verwenden, um Sortier -Sammlungen zu implementieren. System.out.println ("=== sortiert ASC -Nachname ==="); für (Person P: Personlist) {P.printname (); } // Implementierung mit Lambda Expression // Ascending System.out.println ("=== sortiert ASC -Nachame ==="); Collectionss.sort (Personlist, (Person P1, Person p2) -> p1.getSurname (). Vergleiche (p2.getSurname ())); für (Person P: Personlist) {P.printname (); } // Desc Suppequenty System.out.println ("=== sortierter Desc -Nachame ==="); Collectionss.sort (Personlist, (p1, p2) -> p2.getSurname (). Vergleicheto (p1.getSurname ())); für (Person P: Personlist) {P.printname (); }}} public class vergleichste {public static void main (String [] args) {list <Person> personList = person.createShortList (); // Die innere Klasse verwenden, um Sortier -Sammlungen zu implementieren. System.out.println ("=== sortiert ASC -Nachname ==="); für (Person P: Personlist) {P.printname (); } // Implementierung mit Lambda Expression // Ascending System.out.println ("=== sortiert ASC -Nachame ==="); Collectionss.sort (Personlist, (Person P1, Person p2) -> p1.getSurname (). Vergleiche (p2.getSurname ())); für (Person P: Personlist) {P.printname (); } // Desc Suppequenty System.out.println ("=== sortierter Desc -Nachame ==="); Collectionss.sort (Personlist, (p1, p2) -> p2.getSurname (). Vergleicheto (p1.getSurname ())); für (Person P: Personlist) {P.printname (); }}} Sie können sehen, dass anonyme innere Klassen durch Lambda -Ausdrücke implementiert werden können. Beachten Sie, dass der erste Lambda -Ausdruck den Typ des Parameters als Person definiert. Der zweite Lambda -Ausdruck lässt die Typdefinition aus. Lambda -Ausdrücke unterstützen den Typ Knockdown, und wenn der erforderliche Typ im Kontext abgeleitet werden kann, kann die Typdefinition weggelassen werden. Da wir Lambda -Ausdrücke in einem Komparator verwenden, der generische Definitionen verwendet, kann der Compiler diese beiden Parameter als Person ableiten.