it-swarm.com.de

Haben Lambda-Ausdrücke eine andere Verwendung als das Speichern von Codezeilen?

Haben Lambda-Ausdrücke eine andere Verwendung als das Speichern von Codezeilen?

Gibt es spezielle Funktionen von Lambdas, die Probleme lösen, die nicht einfach zu lösen waren? Die typische Verwendung, die ich gesehen habe, ist die, anstatt dies zu schreiben:

Comparator<Developer> byName = new Comparator<Developer>() {
  @Override
  public int compare(Developer o1, Developer o2) {
    return o1.getName().compareTo(o2.getName());
  }
};

Wir können einen Lambda-Ausdruck verwenden, um den Code zu verkürzen:

Comparator<Developer> byName =
(Developer o1, Developer o2) -> o1.getName().compareTo(o2.getName());
117
Vikash

Lambda-Ausdrücke ändern nicht die Menge der Probleme, die Sie mit Java im Allgemeinen lösen können, aber sie erleichtern auf jeden Fall das Lösen bestimmter Probleme, nur aus dem gleichen Grund, aus dem wir nicht mehr in der Assemblersprache programmieren Redundante Aufgaben aus der Arbeit des Programmierers erleichtern das Leben und ermöglichen es, Dinge zu tun, die Sie sonst nicht einmal berühren würden, nur für die Menge an Code, die Sie (manuell) produzieren müssten.

Lambda-Ausdrücke speichern jedoch nicht nur Codezeilen. Mit Lambda-Ausdrücken können Sie Funktionen definieren , für die Sie zuvor anonyme innere Klassen als Workaround verwenden konnten. Deshalb können Sie anonyme innere Klassen in diesen ersetzen Fälle, aber nicht im Allgemeinen.

Vor allem Lambda-Ausdrücke werden unabhängig von der Funktionsschnittstelle definiert, in die sie konvertiert werden. Es gibt also keine geerbten Elemente, auf die sie zugreifen können. Außerdem können sie nicht auf die Instanz des Typs zugreifen, der die Funktionsschnittstelle implementiert. In einem Lambda-Ausdruck haben this und super dieselbe Bedeutung wie im umgebenden Kontext, siehe auch diese Antwort . Sie können auch keine neuen lokalen Variablen erstellen, die lokale Variablen des umgebenden Kontexts spiegeln. Für die beabsichtigte Aufgabe, eine Funktion zu definieren, werden viele Fehlerquellen beseitigt, aber dies impliziert auch, dass es für andere Anwendungsfälle anonyme innere Klassen geben kann, die nicht in einen Lambda-Ausdruck konvertiert werden können, selbst wenn eine funktionale Schnittstelle implementiert wird.

Darüber hinaus garantiert das Konstrukt new Type() { … }, dass eine neue eindeutige Instanz erzeugt wird (wie es new immer tut). Anonyme Instanzen der inneren Klasse behalten immer einen Verweis auf ihre äußere Instanz bei, wenn sie in einem Nicht-Kontext von static erstellt werden. Im Gegensatz dazu erfassen Lambda-Ausdrücke nur bei Bedarf einen Verweis auf this, d. H., Wenn sie auf this oder ein Nicht-Mitglied static zugreifen. Und sie erzeugen Instanzen mit absichtlich nicht angegebener Identität, sodass die Implementierung zur Laufzeit entscheiden kann, ob vorhandene Instanzen wiederverwendet werden sollen (siehe auch „ Erstellt ein Lambda-Ausdruck bei jeder Ausführung ein Objekt auf dem Heap? “ ).

Diese Unterschiede gelten für Ihr Beispiel. Ihr anonymes inneres Klassenkonstrukt erzeugt immer eine neue Instanz, es kann auch einen Verweis auf die äußere Instanz erfassen, während Ihr (Developer o1, Developer o2) -> o1.getName().compareTo(o2.getName()) ist ein nicht erfassender Lambda-Ausdruck, der in typischen Implementierungen als Singleton ausgewertet wird. Außerdem wird kein .class Datei auf Ihrer Festplatte.

Angesichts der Unterschiede in Bezug auf Semantik und Leistung können Lambda-Ausdrücke die Art und Weise verändern, in der Programmierer bestimmte Probleme in Zukunft lösen, natürlich auch aufgrund der neuen APIs, die Ideen der funktionalen Programmierung unter Verwendung der neuen Sprachfunktionen umfassen. Siehe auch Java 8 Lambda-Ausdruck und erstklassige Werte .

114
Holger

Programmiersprachen können von Maschinen nicht ausgeführt werden.

Sie sind für Programmierer zu denken in.

Sprachen sind ein Gespräch mit einem Compiler, um unsere Gedanken in etwas zu verwandeln, das eine Maschine ausführen kann. Eine der Hauptbeschwerden über Java von Leuten, die aus anderen Sprachen zu ihm kommen (oder verlassen es für andere Sprachen ) Früher war es Kräfte ein bestimmtes mentales Modell für den Programmierer (dh alles ist eine Klasse).

Ich werde nicht abwägen, ob das gut oder schlecht ist: Alles ist ein Kompromiss. Aber Java 8 Lambdas ermöglichen Programmierern denken in Bezug auf Funktionen, was in Java bisher nicht möglich war .

Es ist das Gleiche wie ein prozeduraler Programmierer, der lernt, think in Bezug auf Klassen, wenn er nach Java kommt: Sie sehen, wie sie sich allmählich von Klassen entfernen, die verherrlichte Strukturen sind und 'Helfer'-Klassen mit einem Haufen haben von statischen Methoden und weiter zu etwas, das eher einem rationalen OO= Design (mea culpa) ähnelt.

Wenn Sie sie nur als einen kürzeren Weg betrachten, anonyme innere Klassen auszudrücken, werden Sie sie wahrscheinlich nicht so beeindruckend finden, wie der obige prozedurale Programmierer es wahrscheinlich nicht für eine große Verbesserung gehalten hat.

49
Jared Smith

Das Speichern von Codezeilen kann als neues Feature angesehen werden, wenn Sie einen wesentlichen Teil der Logik kürzer und klarer schreiben können, was für andere weniger Zeit zum Lesen und Verstehen erfordert.

Ohne Lambda-Ausdrücke (und/oder Methodenverweise) wären Stream Pipelines viel weniger lesbar gewesen.

Stellen Sie sich zum Beispiel vor, wie die folgende Stream -Pipeline ausgesehen hätte, wenn Sie jeden Lambda-Ausdruck durch eine anonyme Klasseninstanz ersetzt hätten.

List<String> names =
    people.stream()
          .filter(p -> p.getAge() > 21)
          .map(p -> p.getName())
          .sorted((n1,n2) -> n1.compareToIgnoreCase(n2))
          .collect(Collectors.toList());

Es wäre:

List<String> names =
    people.stream()
          .filter(new Predicate<Person>() {
              @Override
              public boolean test(Person p) {
                  return p.getAge() > 21;
              }
          })
          .map(new Function<Person,String>() {
              @Override
              public String apply(Person p) {
                  return p.getName();
              }
          })
          .sorted(new Comparator<String>() {
              @Override
              public int compare(String n1, String n2) {
                  return n1.compareToIgnoreCase(n2);
              }
          })
          .collect(Collectors.toList());

Dies ist viel schwieriger zu schreiben als die Version mit Lambda-Ausdrücken, und es ist viel fehleranfälliger. Es ist auch schwieriger zu verstehen.

Und das ist eine relativ kurze Pipeline.

Um dies ohne Lambda-Ausdrücke und Methodenreferenzen lesbar zu machen, müssten Variablen definiert werden, die die verschiedenen hier verwendeten Funktionsschnittstelleninstanzen enthalten, wodurch die Logik der Pipeline aufgeteilt und das Verständnis erschwert worden wäre.

35
Eran

Interne Iteration

Wenn Sie Java Collections iterieren, neigen die meisten Entwickler dazu, ein Element zu erhalten und dann Verarbeiten Sie das. Nehmen Sie das Objekt heraus und verwenden Sie es dann oder fügen Sie es erneut ein usw. Mit Java-Versionen vor 8 können Sie eine innere Klasse implementieren und folgendermaßen vorgehen:

numbers.forEach(new Consumer<Integer>() {
    public void accept(Integer value) {
        System.out.println(value);
    }
});

Jetzt können Sie mit Java 8 besser und weniger ausführlich vorgehen mit:

numbers.forEach((Integer value) -> System.out.println(value));

oder besser

numbers.forEach(System.out::println);

Verhalten als Argumente

Errate den folgenden Fall:

public int sumAllEven(List<Integer> numbers) {
    int total = 0;

    for (int number : numbers) {
        if (number % 2 == 0) {
            total += number;
        }
    } 
    return total;
}

Mit Java 8 Predicate Interface können Sie das besser machen:

public int sumAll(List<Integer> numbers, Predicate<Integer> p) {
    int total = 0;

    for (int number : numbers) {
        if (p.test(number)) {
            total += number;
        }
    }
    return total;
}

Nennen wir es so:

sumAll(numbers, n -> n % 2 == 0);

Quelle: DZone - Warum wir Lambda-Ausdrücke in Java brauchen

8
alseether

Es gibt viele Vorteile, Lambdas anstelle der inneren Klasse zu verwenden.

  • Machen Sie den Code kompakter und aussagekräftiger, ohne dass Sie mehr Sprachsyntaxsemantik einführen müssen. Sie haben in Ihrer Frage bereits ein Beispiel angegeben.

  • Wenn Sie Lambdas verwenden, programmieren Sie gerne mit Operationen im funktionalen Stil für Elementströme, z. B. Transformationen mit reduzierter Zuordnung für Sammlungen. Siehe Java.util.function & Java.util.stream Dokumentation zu Paketen.

  • Es wurde keine physische Klassendatei für Lambdas vom Compiler generiert. Dadurch werden Ihre gelieferten Anwendungen kleiner. Wie ordnet der Speicher dem Lambda zu?

  • Der Compiler optimiert die Lambda-Erstellung, wenn das Lambda nicht auf Variablen außerhalb seines Gültigkeitsbereichs zugreift. Dies bedeutet, dass die Lambda-Instanz von der JVM nur einmal erstellt wird. Für weitere Details sehen Sie @Holgers Antwort auf die Frage Ist das Cachen von Methodenreferenzen eine gute Idee in Java 8? .

  • Lambdas kann neben der funktionalen Schnittstelle auch Schnittstellen mit mehreren Markern implementieren, die anonymen inneren Klassen können jedoch keine weiteren Schnittstellen implementieren. Beispiel:

    //                 v--- create the lambda locally.
    Consumer<Integer> action = (Consumer<Integer> & Serializable) it -> {/*TODO*/};
    
4
holi-java

Eine Sache, die ich noch nicht erwähnt sehe, ist, dass ein Lambda lässt Sie die Funktionalität dort definieren, wo sie verwendet wird.

Wenn Sie also über eine einfache Auswahlfunktion verfügen, müssen Sie diese nicht an einem separaten Ort mit einem Haufen Heizplatte ablegen, sondern schreiben einfach ein Lambda, das kurz und lokal relevant ist.

2
Carlos

Um Ihre Frage zu beantworten, ist es eine Tatsache, dass Lambdas Sie nicht alles tun lassen, was Sie vor Java-8 nicht konnten, sondern es Ermöglicht es Ihnen, mehr präzisen Code zu schreiben. Dies hat den Vorteil, dass Ihr Code klarer und flexibler wird.

2
Aomine

Ja, es gibt viele Vorteile.

  • Keine Notwendigkeit, eine ganze Klasse zu definieren, wir können die Implementierung der Funktion selbst als Referenz übergeben.
    • Durch die interne Erstellung einer Klasse wird eine .class-Datei erstellt. Wenn Sie Lambda verwenden, wird die Klassenerstellung vom Compiler vermieden, da in Lambda die Funktionsimplementierung anstelle der Klasse übergeben wird.
  • Die Wiederverwendbarkeit von Code ist höher als zuvor
  • Und wie Sie sagten, ist der Code kürzer als die normale Implementierung.
1
Sunil Kanzar

Lambdas sind nur syntaktischer Zucker für anonyme Klassen.

Vor Lambdas können anonyme Klassen verwendet werden, um dasselbe zu erreichen. Jeder Lambda-Ausdruck kann in eine anonyme Klasse konvertiert werden.

Wenn Sie IntelliJ IDEA verwenden, kann es die Konvertierung für Sie durchführen:

  • Setzen Sie den Cursor in das Lambda
  • Drücken Sie Alt/Option + Enter

enter image description here

1
Sweeper