Ist ein Lambda-Ausdruck mehr als eine anonyme innere Klasse mit einer einzigen Methode?

40

Es gibt einen neuen Hype mit den lang erwarteten Lambda-Ausdrücken in Java 8; alle 3 tage erscheint ein weiterer artikel mit ihnen darüber, wie cool sie sind.

Nach meinem Verständnis ist ein Lambda-Ausdruck nichts anderes als eine anonyme innere Klasse mit einer einzigen Methode (zumindest auf Bytecode-Ebene). Abgesehen davon gibt es eine weitere nette Feature - Typ - Inferenz, aber ich glaube, dass dies mit Generika auf einer bestimmten Ebene erreicht werden kann (natürlich nicht so ordentlich wie mit Lambda - Ausdrücken).

Wussten Sie, dass Lambda-Ausdrücke in Java mehr als nur eine syntaktische Zuckerung bringen? Kann ich leistungsfähigere und flexiblere Klassen oder andere objektorientierte Konstrukte mit Lambda-Ausdrücken erstellen, die mit den aktuellen Sprachfunktionen nicht erstellt werden können?

m3th0dman
quelle
30
Wenn eine Sprache vollständig ist, könnte jedes hinzugefügte Merkmal theoretisch als "syntaktischer Zucker" beschrieben werden.
Philipp
34
@Philipp: Das ist falsch. Das bestimmende Merkmal von syntaktischem Zucker ist, dass das Desugarieren rein lokal ist und die globale Struktur des Programms nicht verändert. Es gibt viele Funktionen, die nicht nur mit lokalen Transformationen implementiert werden können. Wenn Sie zum Beispiel eine hypothetische Sprache verwenden, die mit Java identisch ist, aber über geeignete Tail-Aufrufe verfügt, ist es nicht möglich, Tail-Aufrufe für "reines" Java zu deaktivieren, ohne das gesamte Programm global zu transformieren.
Jörg W Mittag
2
@Philipp: Leider gibt es nur eine positive Bewertung für Kommentare, sonst würde ich den Kommentar von Jörg 1000-mal positiv bewerten. Nein, es ist falsch, dass jedes Merkmal als syntaktischer Zucker bezeichnet werden kann. Syntaktischer Zucker fügt keine neue Semantik hinzu. Tatsächlich sind AFAIK die vorgeschlagenen Java-Lambdas KEIN syntaktischer Zucker für spezielle anonyme innere Klassen, da sie unterschiedliche Semantiken haben.
Giorgio,
1
@ Giorgio: und welche unterschiedliche Semantik haben sie?
user102008
2
@ Giorgio: Ja, aber die Konvertierung von thisin OuterClass.thisist Teil des Prozesses, bei dem Lambda-Ausdrücke in anonyme Klassen umgewandelt werden.
user102008

Antworten:

47

tl; dr: Während es sich hauptsächlich um syntaktischen Zucker handelt, macht diese schönere Syntax viele Dinge praktisch, die früher in endlosen, unlesbaren Zeilen von Klammern und Klammern endeten.

Nun, es ist tatsächlich umgekehrt, da Lambdas viel älter als Java sind. Anonyme innere Klassen mit einer einzigen Methode sind (waren) die Java, die Lambdas am nächsten kamen. Es ist eine Annäherung, die für einige Zeit "gut genug" war, aber eine sehr unangenehme Syntax hat.

Auf der Oberfläche scheinen Java 8-Lambdas nicht viel mehr zu sein als syntaktischer Zucker, aber wenn Sie unter die Oberfläche schauen, sehen Sie Tonnen von interessanten Abstraktionen. Zum Beispiel der JVM - Spezifikation behandelt eine Lambda ganz anders als ein „true“ Objekt, und während Sie können behandeln , als ob sie , wo Objekte werden die JVM nicht als solche erforderlich umzusetzen.

Obwohl all diese technischen Tricks interessant und relevant sind (da sie zukünftige Optimierungen in der JVM ermöglichen!), Ist der eigentliche Vorteil "nur" der syntaktische Zuckerteil.

Was ist leichter zu lesen:

myCollection.map(new Mapper<String,String>() {
  public String map(String input) {
    return new StringBuilder(input).reverse().toString();
  }
});

oder:

myCollection.map(element -> new StringBuilder(element).reverse().toString());

oder (mit einem Methodenhandle anstelle eines Lambda):

myCollection.map(String::toUpperCase);

Die Tatsache, dass Sie endlich auf eine prägnante Weise ausdrücken können, bei der es sich zuvor um 5 Codezeilen handelt (von denen 3 äußerst langweilig sind), bringt eine echte Änderung des Praktischen mit sich (aber nicht des Möglichen, Zugegebenen).

Joachim Sauer
quelle
1
Ich stimme dem Syntaxzuckerteil vollkommen zu. meine Frage war nach neuen objektorientierten Konstrukten, die nur mit Lambda-Ausdrücken möglich sind; Immerhin ist Java eine objektorientierte Sprache. Denken Sie im Vergleich zu Generika, und wie sie viel Flexibilität in Java gekauft haben, Flexibilität, die ohne sie nicht erreichbar ist.
m3th0dman
7
@ m3th0dman: eigentlich haben generika keine neuen fähigkeiten hinzugefügt. A Listkann jedes Objekt aufnehmen, A List<String>kann "nur" Strings aufnehmen. Generika haben eine Einschränkung hinzugefügt (und die Option, Einschränkungen zu formalisieren!), Keine Erweiterung der Macht. Seit Java 1.1 war so ziemlich alles syntaktischer Zucker. Und das ist nicht unbedingt eine schlechte Sache.
Joachim Sauer
3
@ m3th0dman: Eigentlich haben Generika keine neuen Fähigkeiten hinzugefügt, weil sie in Java nur ein syntaktischer Zucker sind. A List<String>ist nur ein Listmit Besetzung, Stringum einige Rückgabewerte hinzuzufügen. Trotzdem sind sie äußerst nützlich und haben das Schreiben in der Sprache wesentlich komfortabler gemacht. Lambdas sind ein ähnlicher Fall.
Jan Hudec
3
Eigentlich ist es weit mehr als nur syntaktischer Zucker. Ein Großteil der Funktionalität wird als erstklassiger Bürger in der JVM durch die Verwendung des aufgerufenen dynamischen Bytecodes + einiger ziemlich umfangreicher Fortschritte beim Typ-Inferenz-System implementiert. Es wird nicht als anonyme innere Klasse implementiert.
Martijn Verburg
1
@JanHudec: Nun, sie sind etwas mehr als syntaktischer Zucker, weil der Compiler sie tatsächlich respektiert. Die Laufzeit kümmert sich nicht um sie, das stimmt.
Joachim Sauer
14

Für Java ist es nichts weiter als eine bessere Möglichkeit, eine anonyme innere Klasse zu schaffen. Dies liegt an der fundamentalen Entscheidung in Java, dass jedes Bit des Bytecodes in einer bestimmten Klasse gespeichert werden muss, die nach Jahrzehnten alten Codes nicht mehr geändert werden kann.

Doch darum geht es in Lambda-Ausdrücken nicht wirklich. In Formalismen, in denen es sich eher um einheimische Konzepte als um eine Inszenierung handelt, sind Lambdas grundlegende Bausteine. Sowohl ihre Syntax als auch die Einstellung der Menschen zu ihnen sind sehr unterschiedlich. Das Beispiel einer anonymen rekursiven Funktion, die in Struktur und Interpretation von Computerprogrammen ausschließlich aus Lambda erstellt wurde, kann Ihre gesamte Vorstellung von Berechnung ändern. (Ich bin mir jedoch ziemlich sicher, dass das Erlernen des Programmierens einfach zu esoterisch ist, um jemals eine Mainstream-Erfolgsgeschichte zu werden.)

Kilian Foth
quelle
1
Zu lernen, dass alles in Bezug auf Lambdas implementiert werden kann, ist meiner Meinung nach sehr lehrreich und nicht "esoterisch". (Ich denke jedoch, die meisten "Programmierer" interessieren sich nicht für interessante CS-Theorie.) Das bedeutet nicht, dass Lambdas etwas Besonderes sind. es bedeutet nur, dass Lambdas eine Art universelles Konstrukt sind. Es gibt andere, wie zum Beispiel SKI-Kombinatoren. Lambdas sind grundlegende Bausteine ​​in funktionalen Paradigmen, aber vielleicht kann etwas anderes in einem anderen Paradigma grundlegend sein.
user102008
+1 für "Jump-on-the-Bandwagon Contortion": Ich frage mich oft, warum sich einige Sprachen ständig ändern, um andere populäre Sprachen nachzuahmen, und warum sich Programmierer damit abfinden. Ich mag Lambdas und benutze sie oft in Scala, Lisp, Haskell, aber in Java oder C ++ fühlen sie sich wie ein nachträglicher Einfall in die Sprache, nur weil sie in anderen Sprachen populär geworden sind.
Giorgio
2

Ja, es ist nur ein syntaktischer Zucker, in dem Sinne, dass Sie überall, wo Sie ein Lambda schreiben, diesen Ausdruck mit einer Methode als anonymen inneren Klassenausdruck neu schreiben können, wobei die Klasse die für den Lambda-Kontext abgeleitete funktionale Schnittstelle implementiert, und es wäre semantisch genau gleichwertig. Und Sie können dies tun, indem Sie einfach den Lambda-Ausdruck durch den anonymen Klassenausdruck ersetzen, ohne einen anderen Ausdruck oder eine andere Codezeile zu ändern.

user102008
quelle
1
warum abstimmen? Was ich sagte, ist völlig richtig
user102008
Was Sie schreiben, klingt nach einem vernünftigen Vorschlag für Java Lambdas, aber dieser Vorschlag wurde zugunsten eines anderen abgelehnt ( doanduyhai.wordpress.com/2012/07/12/… ).
Giorgio
1
@ Giorgio: Ich "schlage" nichts vor. Was genau stimmen Sie nicht mit dem überein, was ich gesagt habe? Ja, das Innere des Ausdrucks sieht anders aus, z. B. fügen wir eine Methode hinzu und thismüssen in konvertiert werden OuterClass.this. Das widerspricht nicht dem, was ich gesagt habe - jeder Lambda- Ausdruck kann in einen semantisch äquivalenten anonymen Klassenausdruck umgewandelt werden , ohne dass etwas außerhalb dieses Ausdrucks geändert wird . Ich habe nicht gesagt, dass sich das Innere nicht ändern würde.
user102008
3
Was Sie gesagt haben, ist falsch. Die Semantik der inneren Klassen von Lambda und Anon ist nicht genau gleich (obwohl sie ähnlich sind). Aus der Spitze von meinem Kopf, die Bedeutung der Bedeutung dieser Änderungen; und anon innere Klassen führen immer zu einer neuen Instanz eines Objekts, während ein Lambda möglicherweise oder möglicherweise nicht.
Stuart Marks
1
@StuartMarks: Nein, du hast meine Antwort nicht gelesen. Ich habe nicht gesagt, dass jeder von ihnen alles kann, was der andere tut. Ich sagte, dass ein Lambda eine äquivalente anonyme Klasse hat, die semantisch gleich ist. Wie ich bereits in Kommentaren sagte, ist thisLambda ein Teil des syntaktischen Zuckers und kein Unterschied in der Semantik. Und über Unterschiede in der Implementierung, Implementierung! = Semantik. Über Unterschiede in der Objektidentität; Die Objektidentität ist äußerst tangential zur Funktionalität von Lambdas. niemand überprüft die Objektidentität von Lambdas / Anon-Klassen, da dies nicht sinnvoll ist.
user102008
1

Joachim Sauer hat Ihre Frage bereits gut beantwortet und nur angedeutet, was ich für wichtig halte. Da Lambdas keine Klassen sind, werden sie auch nicht als solche kompiliert. Alle anonymen inneren Klassen führen zur Erstellung einer .class-Datei, die wiederum vom ClassLoader geladen werden muss. Die Verwendung von Lambdas verschönert also nicht nur Ihren Code, sondern verringert auch die Größe Ihres kompilierten Codes, den Speicherbedarf Ihres ClassLoader und die Zeit, die für die Übertragung der Bits von Ihrer Festplatte benötigt wird.

Christoph Grimmer-Dietrich
quelle
-1

Nein sind sie nicht.

Ein anderer Weg ist, Lambdas sind ein nicht objektorientierter, aber prägnanter Weg, um dasselbe zu erreichen, was eine anonyme Klasse oder, wie ich es vorziehen würde, eine innere Klasse auf objektorientierte Weise erreichen würde.

Guido Anselmi
quelle
1
Die funktionale Programmierung kann vollständig orthogonal zur objektorientierten Programmierung sein (siehe: Scala und F #). Das liest sich wie die Meinung von jemandem, der die funktionale Programmierung aus Prinzip einfach nicht mag.
KChaloux
1
@KChaloux - Kein Hasser für funktionale Programmierung. Die Antwort wird von jemandem geschrieben, der lieber einen Hammer UND einen Schraubenzieher als einen Hammerzieher hat. OO-Programmierung ist ein gutes Paradigma, ebenso wie funktionale Programmierung. Meine Präferenz in einer Sprache ist es, klar über ihre Absichten zu sein. Lambdas Ich fühle mich die ansonsten OO Natur von Java matschig. Java wurde nicht als funktionale Sprache konzipiert. Menschen brauchen Struktur, um ihre beste Arbeit zu leisten.
Guido Anselmi