English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Tiefgründige Analyse von JDK8Neue Funktionen: Lambda-Ausdrücke

Das erste Mal habe ich Lambda-Ausdrücke in TypeScript (dem Übersatz von JavaScript) kennengelernt, damals, um sicherzustellen, dass das this-Methode von TypeScript außerhalb und nicht innerhalb der eigenen Methode verwendet wird. Nach der Verwendung dachte ich plötzlich, Lambda ist nicht JDK8eine neue Funktion hinzuzufügen? Also dachte ich mir, ich sollte nach entsprechenden Informationen suchen und sie notieren:

1. Verhaltensparameterisierung

Verhaltensparameterisierung, kurz gesagt, besteht darin, dass der Körper der Funktion nur allgemeine Code der Musterklasse enthält, während einige Logiken, die mit dem Geschäftsfall variieren, als Parameter an die Funktion weitergegeben werden. Mit der Verhaltensparameterisierung wird das Programm allgemeiner und kann auf häufig wechselnde Anforderungen reagieren.

Denken wir uns eine Geschäftsfall-Szene, angenommen, wir müssen Äpfel durch ein Programm filtern, wir definieren zunächst eine Apfel-Entität:

public class Apple {
/** Kennung */
private long id;
/** Farbe */
private Color color;
/** Gewicht */
private float weight;
/** Anbaugebiet */
private String origin;
public Apple() {
}
public Apple(long id, Color color, float weight, String origin) {
this.id = id;
this.color = color;
this.weight = weight;
this.origin = origin;
}
// Getter und Setter werden weggelassen
}

Die ursprünglichen Anforderungen des Benutzers könnten darin bestehen, dass der Benutzer einfach durch das Programm grüne Äpfel filtern kann, daher können wir das Programm schnell umsetzen:

public static List<Apple> filterGreenApples(List<Apple> apples) {
List<Apple> filterApples = new ArrayList<>();
for (final Apple apple : apples) {
if (Color.GREEN.equals(apple.getColor())) {
filterApples.add(apple);}}
}
}
return filterApples;
}

Dieser Code ist einfach und hat nicht viel zu sagen. Wenn die Benutzeranforderung jedoch auf Grün wird, scheint die Änderung des Codes ebenfalls einfach zu sein, nur die grüne Bewertungskondition in Rot zu ändern. Aber wir müssen eine andere Frage berücksichtigen, wenn die Bedingungen häufig ändern?63; Wenn es sich nur um eine Änderung der Farbe handelt, dann gut, lassen Sie den Benutzer die Bedingungen der Farberkennung direkt einfügen, die Parameter der Bewertungsmethode werden "zu bewertende Sammlung" und "zu filternde Farbe".63; Zu diesem Zeitpunkt können wir Verhaltensparameterisieren und die Filterkriterien extrahieren und als Parameter einfügen, zu diesem Zeitpunkt können wir eine abstrakte Schnittstelle für die Bewertung verpacken:

public interface AppleFilter {
/**
* Abstrakte Filterbedingung
*
* @param apple
* @return
*/
boolean accept(Apple apple);
}
/**
* Denken Sie an die Verpackung von Filterkriterien als Schnittstelle
*
* @param apples
* @param filter
* @return
*/
public static List<Apple> filterApplesByAppleFilter(List<Apple> apples, AppleFilter filter) {
List<Apple> filterApples = new ArrayList<>();
for (final Apple apple : apples) {
if (filter.accept(apple)) {
filterApples.add(apple);}}
}
}
return filterApples;
}

Nach der Abstraktion der obigen Aktion können wir die Filterkriterien an der Stelle der tatsächlichen Aufrufe festlegen und die Kriterien als Parameter an die Methode übergeben. In diesem Fall wird das Verfahren der anonymen internen Klasse verwendet:

public static void main(String[] args) {
List<Apple> apples = new ArrayList<>();
// Filter für Äpfel
List<Apple> filterApples = filterApplesByAppleFilter(apples, new AppleFilter() {
@Override
public boolean accept(Apple apple) {
// Filtern nach Gewicht größer als100g roter Apfel
return Color.RED.equals(apple.getColor()) && apple.getWeight() > 100;
}
});
}

Dieser Designansatz wird im JDK intern oft verwendet, z.B. Java.util.Comparator, java.util.concurrent.Callable u.v.m. Wenn wir solche Schnittstellen verwenden, können wir die spezifische Ausführungslogik der Funktion an der Stelle der tatsächlichen Aufrufe durch anonyme Klassen angeben. Obwohl der obige Codeblock sehr technisch ist, ist er nicht besonders kurz, in java8Wir können durch Lambda vereinfachen:

// Filter für Äpfel
List<Apple> filterApples = filterApplesByAppleFilter(apples,
(Apple apple) -> Color.RED.equals(apple.getColor()) && apple.getWeight() >= 100);
//()->xxx () enthalten die Methodenparameter, xxx ist die Methodenimplementierung

Zwei. Lambda-Ausdrücke-Definition

Wir können Lambda-Ausdrücke als eine art kürzere, übertragbare anonyme Funktion definieren. Zunächst müssen wir klarstellen, dass Lambda-Ausdrücke im Wesentlichen Funktionen sind, obwohl sie nicht zu einer bestimmten Klasse gehören, verfügen sie jedoch über eine Parameterliste, einen Funktionskörper, einen Rückgabetyp und können Ausnahmen werfen; zweitens sind sie anonym, Lambda-Ausdrücke haben keinen spezifischen Funktionsnamen; Lambda-Ausdrücke können wie Parameter übertragen werden, was die Code-Schreibweise erheblich vereinfacht. Die Formatdefinition ist wie folgt:

Format eins: Parameterliste -> Ausdruck

Format zwei: Parameterliste -> {Ausdruckskollektion}

Zu beachten ist, dass Lambda-Ausdrücke implizit den return-Schlüssel beinhalten, daher ist es in einfachen Ausdrücken nicht erforderlich, den Schlüssel return ausdrücklich zu schreiben. Wenn der Ausdruck jedoch eine Sammlung von Anweisungen ist, muss return explizit hinzugefügt werden, und mehrere Ausdrücke werden durch geschweifte Klammern { } umgeben. Einige Beispiele sind hier zu finden:

//Gibt die Länge des angegebenen Strings zurück, implizites return-Satz
(String s) -> s.length() 
// immer zurück42ohne Parameter Methode
() -> 42 
// Falls es mehrere Zeilen ausdrücke gibt, werden sie in geschweifte Klammern gesetzt
(int x, int y) -> {
int z = x * y;
return x + z;
}

Drittes Kapitel: Nutzung von Lambda-Ausdrücken basierend auf funktionalen Schnittstellen

Der Einsatz von Lambda-Ausdrücken erfordert die Hilfe funktionaler Schnittstellen, das bedeutet, dass Lambda-Ausdrücke nur in Bereichen verwendet werden können, in denen funktionale Schnittstellen auftreten, um sie zu vereinfachen.

Benutzerdefinierte funktionale Schnittstelle:

Funktionale Schnittstelle ist definiert als Schnittstelle, die nur eine abstrakte Methode hat. java8Die Verbesserung in der Schnittstellendefinition ist die Einführung von Standardmethoden, die es uns ermöglichen, im Schnittstellenbereich Methoden mit Standardimplementierungen bereitzustellen. Aber egal wie viele Standardmethoden vorhanden sind, solange es eine und nur eine abstrakte Methode gibt, ist es eine funktionale Schnittstelle, wie im obigen AppleFilter gezeigt:

/**
* AppleFilter-Schnittstelle
*/
@FunctionalInterface
public interface AppleFilter {
/**
* Abstrakte Filterbedingung
*
* @param apple
* @return
*/
boolean accept(Apple apple);
}

AppleFilter enthält nur eine abstrakte Methode accept(Apple apple) und kann gemäß der Definition als funktionaler Schnittstelle betrachtet werden. Beim Definieren dieses Schnittstellen haben wir die @FunctionalInterface-Annotation hinzugefügt, um zu kennzeichnen, dass es sich um eine funktionale Schnittstelle handelt. Diese Annotation ist optional. Wenn diese Schnittstelle hinzugefügt wird, beschränkt der Compiler die Schnittstelle darauf, nur eine abstrakte Methode zu haben, andernfalls wird ein Fehler ausgegeben. Daher wird empfohlen, die Annotation für funktionale Schnittstellen hinzuzufügen.

Die funktionalen Schnittstellen der JDK:

Die JDK hat bereits reichhaltige funktionalen Schnittstellen für Lambda-Ausdrücke integriert. Nachfolgend werden Beispiele zur Verwendung von Predicate<T>, Consumer<T> und Function<T, R> dargestellt.

Predicate:

@FunctionalInterface
public interface Predicate<T> {
/**
* Bewertet dieses Prädikat anhand des angegebenen Arguments.
*
* @param t das Eingangsargument
* @return @code {true}, wenn der Eingabeparameter dem Prädikat entspricht,
* sonst @code {false}
*/
boolean test(T t);
}

Die Funktion von Predicate ähnelt der obigen AppleFilter, sie überprüft die übergebenen Parameter anhand der im externen Bereich festgelegten Bedingungen und gibt das Überprüfungsresultat boolean zurück. Unten wird der Predicate zur Filterung der Elemente der List-Sammlung verwendet:

/**
*
* @param list
* @param predicate
* @param <T>
* @return
*/
public <T> List<T> filter(List<T> list, Predicate<T> predicate) {
List<T> newList = new ArrayList<T>();
for (final T t : list) {
if (predicate.test(t)) {
newList.add(t);
}
}
return newList;
}

Verwenden Sie:

demo.filter(list, (String str -> null != str && !str.isEmpty());

Consumer

@FunctionalInterface
public interface Consumer<T> {
/**
* Führt diese Operation auf das angegebene Argument aus.
*
* @param t das Eingangsargument
*/
void accept(T t);
}

Consumer bietet eine abstrakte Funktion accept, die Parameter annimmt, aber keinen Wert zurückgibt. Below wird Consumer verwendet, um die Sammlung zu durchsuchen.

/**
* Durchsucht die Sammlung und führt ein benutzerdefiniertes Verhalten aus
*
* @param list
* @param consumer
* @param <T>
*/
public <T> void filter(List<T> list, Consumer<T> consumer) {
for (final T t : list) {
consumer.accept(t);
}
}

Mit dem obigen funktionalen Interface wird die Zeichenfolgensammlung durchsucht und nicht leere Zeichenfolgen gedruckt:

demo.filter(list, (String str -> {
if (StringUtils.isNotBlank(str)) {
System.out.println(str);
}
});

Function

@FunctionalInterface
public interface Function<T, R> {
/**
* Wendet diese Funktion auf das angegebene Argument an.
*
* @param t der Funktionsargument
* @return das Funktionsergebnis
*/
R apply(T t);
}

Die Funktion führt die Umwandlung durch, der Eingang ist Daten des Typs T, und die Rückgabe ist Daten des Typs R. Below wird die Funktion Function verwendet, um die Sammlung umzuwandeln:

public <T, R> List<R> filter(List<T> list, Function<T, R> function) {
List<R> newList = new ArrayList<R>();
for (final T t : list) {
newList.add(function.apply(t));
}
return newList;
}

Andere:

demo.filter(list, (String str -> Integer.parseInt(str));

Diese funktionalen Schnittstellen bieten auch einige Standardimplementierungen für logische Operationen, die später im Java vorgestellt werden.8Die Standardmethoden der Schnittstelle werden später besprochen.

Einige Dinge, die bei der Verwendung zu beachten sind:

Typinferenz:

Während des Codings kann es manchmal verwirrend sein, dass unser Aufrufcode spezifisch welche funktionalen Schnittstellen abstimmt. Tatsächlich wird der Compiler nach den Parametern, dem Rückgabetyp und dem Ausnahme-Typ (wenn vorhanden) korrekt beurteilen.
Bei der konkreten Aufrufung kann in einigen Fällen der Parameter-Typ weggelassen werden, um den Code weiter zu vereinfachen:

// Filter für Äpfel
List<Apple> filterApples = filterApplesByAppleFilter(apples,
(Apple apple) -> Color.RED.equals(apple.getColor()) && apple.getWeight() >= 100);
// In einigen Fällen können wir sogar die Parameterarten weglassen, da der Compiler die Parameterarten aufgrund des Kontexts korrekt beurteilen kann
List<Apple> filterApples = filterApplesByAppleFilter(apples,
apple -> Color.R
> ED.equals(apple.getColor()) && apple.getWeight() >= 100);

Lokale Variable

In allen vorherigen Beispielen haben wir Lambda-Ausdrücke verwendet, die ihre Hauptparameter verwenden. Wir können auch lokale Variablen im Lambda-Ausdruck verwenden, wie folgt:

int weight = 100;
List<Apple> filterApples = filterApplesByAppleFilter(apples,
apple -> Color.RED.equals(apple.getColor()) && apple.getWeight() >= weight);:

In diesem Beispiel verwenden wir im Lambda-Ausdruck die lokale Variable weight, aber es muss erforderlich sein, dass die Variable im Lambda-Ausdruck explizit als final oder faktisch final deklariert wird. Dies liegt daran, dass lokale Variablen im Stack gespeichert werden und der Lambda-Ausdruck in einem anderen Thread ausgeführt wird. Wenn dieser Thread versucht, auf die lokale Variable zuzugreifen, besteht die Möglichkeit, dass die Variable geändert oder回收 wird, daher gibt es kein Problem mit der Thread-Sicherheit, wenn sie mit final versehen wird.

IV. Methodenreferenzen

Mit Methodenreferenzen kann der Code weiter vereinfacht werden, manchmal macht diese Vereinfachung den Code auch direkter aussehend. Sehen wir uns ein Beispiel an:

/* ... Die Initialisierung von apples wird weggelassen */
// Die Verwendung von Lambda-Ausdrücken
apples.sort((Apple a, Apple b)}) -> Float.compare(a.getWeight(), b.getWeight()));
// Verwendung von Methodenreferenzen
apples.sort(Comparator.comparing(Apple::getWeight));

Methodenreferenzen verbinden die Zugehörigkeit der Methode und die Methode selbst durch :: und werden in drei Hauptkategorien unterteilt:

Statische Methode

(args) -> ClassName.staticMethod(args)

wird umgewandelt in

ClassName::staticMethod

Instanzmethode der Parameter

(args) -> args.instanceMethod()

wird umgewandelt in

> ClassName::instanceMethod // ClassName ist der Typ von args

externer Instanzmethode

(args) -> ext.instanceMethod(args)

wird umgewandelt in

ext::instanceMethod(args)

Referenz:

http://www.codeceo.com/Artikel/lambda-von-java-8.html

Das, was ich Ihnen gezeigt habe, ist das JDK, das ich Ihnen vorgestellt habe8Die neuen Funktionen der Lambda-Ausdrücke hoffen wir, helfen Ihnen weiter. Falls Sie Fragen haben, hinterlassen Sie mir bitte eine Nachricht, ich werde mich so schnell wie möglich bei Ihnen melden. Ich danke auch sehr für die Unterstützung unserer Seite呐喊教程!

Erklärung: Der Inhalt dieses Artikels wurde aus dem Internet entnommen und gehört dem Urheberrecht des jeweiligen Autors. Der Inhalt wurde von Internetnutzern freiwillig beigesteuert und hochgeladen. Diese Seite besitzt keine Eigentumsrechte und hat den Inhalt nicht manuell bearbeitet. Sie übernimmt auch keine rechtlichen Verantwortlichkeiten. Falls Sie auf Inhalte stoßen, die möglicherweise urheberrechtlich geschützt sind, freuen wir uns über eine E-Mail an: notice#oldtoolbag.com (bei der E-Mail bitte das # durch @ ersetzen) zur Meldung von Missbrauch und zur Bereitstellung relevanter Beweise. Bei nachgewiesener Täuschung wird diese Seite sofort den verdächtigen Inhalt löschen.

Dir gefällt vielleicht auch