Java-Lambda-Ausdrücke
Java-Lambda-Ausdrücke
In diesem Artikel erfahren wir anhand von Beispielen mehr über Java-Lambda-Ausdrücke und die Verwendung von Lambda-Ausdrücken mit funktionalen Schnittstellen, generischen funktionalen Schnittstellen und Stream-API.
Der Lambda-Ausdruck wurde erstmals in Java 8 eingeführt. Sein Hauptziel ist es, die Ausdruckskraft der Sprache zu erhöhen.
Aber bevor wir uns mit Lambdas befassen, müssen wir zuerst funktionale Schnittstellen verstehen.
Was ist eine funktionale Schnittstelle?
Wenn eine Java-Schnittstelle genau eine abstrakte Methode enthält, wird sie als funktionale Schnittstelle bezeichnet. Diese einzige Methode gibt den Verwendungszweck der Schnittstelle an.
Zum Beispiel Runnable
Schnittstelle aus Paket java.lang
; ist eine funktionale Schnittstelle, weil sie nur eine Methode darstellt, d. h. run()
.
Beispiel 1:Definieren Sie eine funktionale Schnittstelle in Java
import java.lang.FunctionalInterface;
@FunctionalInterface
public interface MyInterface{
// the single abstract method
double getValue();
}
Im obigen Beispiel hat das Interface MyInterface nur eine abstrakte Methode getValue(). Daher ist es eine funktionale Schnittstelle.
Hier haben wir die Anmerkung @FunctionalInterface
verwendet . Die Annotation zwingt den Java-Compiler anzugeben, dass die Schnittstelle eine funktionale Schnittstelle ist. Daher darf nicht mehr als eine abstrakte Methode verwendet werden. Es ist jedoch nicht obligatorisch.
In Java 7 wurden funktionale Schnittstellen als Single Abstract Methods oder SAM betrachtet Typ. SAMs wurden üblicherweise mit anonymen Klassen in Java 7 implementiert.
Beispiel 2:Implementieren Sie SAM mit anonymen Klassen in Java
public class FunctionInterfaceTest {
public static void main(String[] args) {
// anonymous class
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("I just implemented the Runnable Functional Interface.");
}
}).start();
}
}
Ausgabe :
I just implemented the Runnable Functional Interface.
Hier können wir eine anonyme Klasse an eine Methode übergeben. Dies hilft beim Schreiben von Programmen mit weniger Code in Java 7. Die Syntax war jedoch immer noch schwierig und es waren viele zusätzliche Codezeilen erforderlich.
Java 8 erweiterte die Leistungsfähigkeit eines SAMs, indem es noch einen Schritt weiter ging. Da wir wissen, dass eine funktionale Schnittstelle nur eine Methode hat, sollte es nicht nötig sein, den Namen dieser Methode zu definieren, wenn sie als Argument übergeben wird. Mit dem Lambda-Ausdruck können wir genau das tun.
Einführung in Lambda-Ausdrücke
Der Lambda-Ausdruck ist im Wesentlichen eine anonyme oder unbenannte Methode. Der Lambda-Ausdruck wird nicht alleine ausgeführt. Stattdessen wird es verwendet, um eine Methode zu implementieren, die durch eine funktionale Schnittstelle definiert ist.
Wie definiere ich einen Lambda-Ausdruck in Java?
So können wir Lambda-Ausdrücke in Java definieren.
(parameter list) -> lambda body
Der neue Operator (->
) wird als Pfeiloperator oder Lambdaoperator bezeichnet. Die Syntax ist im Moment möglicherweise nicht klar. Sehen wir uns einige Beispiele an,
Angenommen, wir haben eine Methode wie diese:
double getPiValue() {
return 3.1415;
}
Wir können diese Methode mit dem Lambda-Ausdruck schreiben als:
() -> 3.1415
Hier hat die Methode keine Parameter. Daher enthält die linke Seite des Operators einen leeren Parameter. Die rechte Seite ist der Lambda-Körper, der die Aktion des Lambda-Ausdrucks angibt. In diesem Fall gibt es den Wert 3.1415 zurück.
Arten von Lambda-Körpern
In Java gibt es zwei Arten von Lambda-Body.
1. Ein Körper mit einem einzigen Ausdruck
() -> System.out.println("Lambdas are great");
Diese Art von Lambda-Körper wird als Ausdruckskörper bezeichnet.
2. Ein Körper, der aus einem Codeblock besteht.
() -> {
double pi = 3.1415;
return pi;
};
Diese Art des Lambda-Körpers ist als Blockkörper bekannt. Der Blockkörper ermöglicht es dem Lambda-Körper, mehrere Anweisungen einzuschließen. Diese Anweisungen sind in geschweiften Klammern eingeschlossen und Sie müssen nach den geschweiften Klammern ein Semikolon hinzufügen.
Hinweis :Für den Blockkörper können Sie eine return-Anweisung haben, wenn der Körper einen Wert zurückgibt. Der Ausdruckskörper erfordert jedoch keine return-Anweisung.
Beispiel 3:Lambda-Ausdruck
Lassen Sie uns ein Java-Programm schreiben, das den Wert von Pi mithilfe des Lambda-Ausdrucks zurückgibt.
Wie bereits erwähnt, wird ein Lambda-Ausdruck nicht alleine ausgeführt. Vielmehr bildet es die Implementierung der abstrakten Methode, die durch die funktionale Schnittstelle definiert ist.
Also müssen wir zuerst eine funktionale Schnittstelle definieren.
import java.lang.FunctionalInterface;
// this is functional interface
@FunctionalInterface
interface MyInterface{
// abstract method
double getPiValue();
}
public class Main {
public static void main( String[] args ) {
// declare a reference to MyInterface
MyInterface ref;
// lambda expression
ref = () -> 3.1415;
System.out.println("Value of Pi = " + ref.getPiValue());
}
}
Ausgabe :
Value of Pi = 3.1415
Im obigen Beispiel
- Wir haben eine funktionale Schnittstelle namens MyInterface erstellt . Es enthält eine einzelne abstrakte Methode namens
getPiValue()
- Innerhalb des Hauptgebäudes Klasse haben wir eine Referenz auf MyInterface deklariert . Beachten Sie, dass wir eine Referenz einer Schnittstelle deklarieren können, aber wir können keine Schnittstelle instanziieren. Das ist,
// it will throw an error MyInterface ref = new myInterface(); // it is valid MyInterface ref;
- Dann haben wir der Referenz einen Lambda-Ausdruck zugewiesen.
ref = () -> 3.1415;
- Schließlich rufen wir die Methode
getPiValue()
auf über die Referenzschnittstelle. Wann
System.out.println("Value of Pi = " + ref.getPiValue());
Lambda-Ausdrücke mit Parametern
Bisher haben wir Lambda-Ausdrücke ohne Parameter erstellt. Ähnlich wie Methoden können Lambda-Ausdrücke jedoch auch Parameter haben. Zum Beispiel
(n) -> (n%2)==0
Hier ist die Variable n in der Klammer ein Parameter, der an den Lambda-Ausdruck übergeben wird. Der Lambda-Körper nimmt den Parameter und prüft, ob er gerade oder ungerade ist.
Beispiel 4:Lambda-Ausdruck mit Parametern verwenden
@FunctionalInterface
interface MyInterface {
// abstract method
String reverse(String n);
}
public class Main {
public static void main( String[] args ) {
// declare a reference to MyInterface
// assign a lambda expression to the reference
MyInterface ref = (str) -> {
String result = "";
for (int i = str.length()-1; i >= 0 ; i--)
result += str.charAt(i);
return result;
};
// call the method of the interface
System.out.println("Lambda reversed = " + ref.reverse("Lambda"));
}
}
Ausgabe :
Lambda reversed = adbmaL
Generische Funktionsschnittstelle
Bisher haben wir die funktionale Schnittstelle verwendet, die nur einen Werttyp akzeptiert. Zum Beispiel
@FunctionalInterface
interface MyInterface {
String reverseString(String n);
}
Die obige funktionale Schnittstelle akzeptiert nur String
und gibt String
zurück . Wir können die funktionale Schnittstelle jedoch generisch machen, sodass jeder Datentyp akzeptiert wird. Wenn Sie sich bei Generika nicht sicher sind, besuchen Sie Java Generics.
Beispiel 5:Generische funktionale Schnittstelle und Lambda-Ausdrücke
// GenericInterface.java
@FunctionalInterface
interface GenericInterface<T> {
// generic method
T func(T t);
}
// GenericLambda.java
public class Main {
public static void main( String[] args ) {
// declare a reference to GenericInterface
// the GenericInterface operates on String data
// assign a lambda expression to it
GenericInterface<String> reverse = (str) -> {
String result = "";
for (int i = str.length()-1; i >= 0 ; i--)
result += str.charAt(i);
return result;
};
System.out.println("Lambda reversed = " + reverse.func("Lambda"));
// declare another reference to GenericInterface
// the GenericInterface operates on Integer data
// assign a lambda expression to it
GenericInterface<Integer> factorial = (n) -> {
int result = 1;
for (int i = 1; i <= n; i++)
result = i * result;
return result;
};
System.out.println("factorial of 5 = " + factorial.func(5));
}
}
Ausgabe :
Lambda reversed = adbmaL factorial of 5 = 120
Im obigen Beispiel haben wir eine generische funktionale Schnittstelle mit dem Namen GenericInterface erstellt . Es enthält eine generische Methode namens func()
.
Hier, innerhalb der Hauptklasse,
GenericInterface<String> reverse
- erstellt einen Verweis auf die Schnittstelle. Die Schnittstelle arbeitet jetzt aufString
Art der Daten.GenericInterface<Integer> factorial
- erstellt einen Verweis auf die Schnittstelle. Die Schnittstelle arbeitet in diesem Fall auf demInteger
Art der Daten.
Lambda-Ausdruck und Stream-API
Das neue java.util.stream-Paket wurde zu JDK8 hinzugefügt, das es Java-Entwicklern ermöglicht, Operationen wie Suchen, Filtern, Zuordnen, Reduzieren oder Bearbeiten von Sammlungen wie Lists
durchzuführen .
Zum Beispiel haben wir einen Datenstrom (in unserem Fall ein List
von String
), wobei jede Zeichenfolge eine Kombination aus Ländername und Ort des Landes ist. Jetzt können wir diesen Datenstrom verarbeiten und nur die Orte aus Nepal abrufen.
Dazu können wir Massenoperationen im Stream durch die Kombination von Stream-API und Lambda-Ausdruck durchführen.
Beispiel 6:Demonstration der Verwendung von Lambdas mit der Stream-API
import java.util.ArrayList;
import java.util.List;
public class StreamMain {
// create an object of list using ArrayList
static List<String> places = new ArrayList<>();
// preparing our data
public static List getPlaces(){
// add places and country to the list
places.add("Nepal, Kathmandu");
places.add("Nepal, Pokhara");
places.add("India, Delhi");
places.add("USA, New York");
places.add("Africa, Nigeria");
return places;
}
public static void main( String[] args ) {
List<String> myPlaces = getPlaces();
System.out.println("Places from Nepal:");
// Filter places from Nepal
myPlaces.stream()
.filter((p) -> p.startsWith("Nepal"))
.map((p) -> p.toUpperCase())
.sorted()
.forEach((p) -> System.out.println(p));
}
}
Ausgabe :
Places from Nepal: NEPAL, KATHMANDU NEPAL, POKHARA
Beachten Sie im obigen Beispiel die Anweisung,
myPlaces.stream()
.filter((p) -> p.startsWith("Nepal"))
.map((p) -> p.toUpperCase())
.sorted()
.forEach((p) -> System.out.println(p));
Hier verwenden wir Methoden wie filter()
, map()
und forEach()
der Stream-API. Diese Methoden können einen Lambda-Ausdruck als Eingabe annehmen.
Wir können auch unsere eigenen Ausdrücke basierend auf der oben gelernten Syntax definieren. Dadurch können wir die Codezeilen drastisch reduzieren, wie wir im obigen Beispiel gesehen haben.
Java