Ich verstehe Schließung und habe in einigen Sprachen wie Python und SML angewendet. Dennoch, wenn ich wikipedia über Schließung in Java (natürlich, nur Version 8) lese, verstehe ich keinen Unterschied, ob Java Schließung oder nicht in ihrem Beispiel unterstützt.
Dieser Code kopiere ich aus Wikipedia: Closure
Der Java-Code ohne Abschluss:
%Vor%Und wenn Java die Schließung unterstützt, sieht der Code wie folgt aus:
%Vor%Also, meine Frage ist: Wenn Java die Schließung unterstützt, welche spezielle Sache im zweiten Code? Ich sehe wirklich keinen Hauptunterschied zwischen zwei Codes.
Bitte sagen Sie mir diesen Punkt.
Danke :)
Der Unterschied ist der:
Sie erreichen das gleiche Ziel, aber die Lambda-Syntax ist sicher leichter.
Ein Lambda kann jedoch nur auf lokale Variablen in dem Bereich zugreifen, in dem es deklariert ist, wenn sie final
(oder effektiv endgültig) sind; siehe 15.27.2 in JSR-000335, Übersichtsvorlage Nr. 2. Man könnte also argumentieren, dass Java-Lambdas keine vollständigen Schließungen sind.
Aber die JSR-000335-Spezifikation impliziert, dass Lambdas keine anonymen Klassen sind. Und this
und super
in einem Lambda-Körper haben eine andere Bedeutung, die sie in einer Methode haben.
Die Spezifikation beschreibt lambdas (an einem Punkt) als implementiert unter Verwendung von "synthetischen Klassen" und gibt an, dass eine Instanz der synthetischen Klassen gegebenenfalls als Kompilierungsoptimierung wiederverwendet werden kann. (Im Gegensatz dazu wäre es dem Compiler nicht möglich, diese Optimierung für eine anonyme Klasse vorzunehmen.) Diese Optimierung bedeutet, dass Lambdas wahrscheinlich sind, um eine bessere Leistung zu erbringen als die mit anonymen Klassen kodierte.
Der Punkt ist, dass sie funktional nicht wirklich so verschieden sind:
%Vor%entspricht einer neuen Klasseninstanz von Runnable mit einer entsprechenden Implementierung der run () -Methode. Sie ersetzen eine Menge "Boiler-Plate" Code durch eine einfache Lambda Funktion.
Mit dem Lambda wird Ihr Code viel aussagekräftiger, prägnanter, einfacher zu schreiben und sehr lesbar. Und genau hier beginnen die Vorteile, sobald Sie die Welt der Schließungen und Lambda-Funktionen betreten.
Nun, es gibt hauptsächlich zwei Unterschiede. Der erste ist mit dem zugrunde liegenden Prozess verbunden, der in Javac und der JVM passiert, und der zweite ist die Syntax.
Bevor wir eine Erklärung geben, wollen wir sehr schnell definieren, was eine Schlussfolgerung in einer "echten" funktionalen Sprache ist: eine Funktion, die mit ihrer lexikalischen Umgebung und ihrem Speicherkontext einhergeht.
Zunächst wird der Compiler diesen Code auflösen, indem er den 'syntaxiq-Kontext' durchsucht, in dem der Code ausgeführt wird (nicht die Variablen, aber der Code, den Sie schreiben, bewirkt, dass der Compiler statische Prüfungen durchführt). Wenn Sie feststellen, dass Sie ein Lambda als Parameter einer Methode schreiben, die auf eine funktionale Schnittstelle wartet, können Sie kompilieren, sodass die JVM den richtigen Codecode zur Ausführung finden kann. In der Tat werden Sie eine Schnittstelle implementieren, die nur eine Signatur im laufenden Betrieb verfügbar macht.
Dann ist die Syntax selbst wichtig, weil sie Ihnen nicht die gleiche Macht wie eine anonyme Klasse geben wird, wo Sie mehrere Methoden an derselben Stelle implementieren oder überschreiben können.
Das Stück Code in der Frage ist nicht sehr relevant, um den Unterschied zwischen den beiden Konzepten zu sehen, und es verwendet nicht "closure" in seiner Gesamtheit. Hier ist ein Code, der helfen könnte, das Potenzial zu sehen:
%Vor% Dies kompiliert in Java 8 und wird gedruckt:
20
Aber es gibt etwas Seltsames in Java mit Schließung. Dieser Code stammt aus der Online-Dokumentation , Listing 14:
%Vor% Dies wird gedruckt
Howdy, world!
Und genau das wollen Sie in einer funktionalen Programmiersprache nicht. Die JVM tut dies, weil Java eine objektorientierte Sprache ist und ziemlich gut darin ist.
In Java verwendet man viele Referenzen auf den Heap (die in der Nähe von Zeigern in einer Sprache wie C oder dem noch näher liegenden C ++ liegen). Mit diesem Prinzip können Sie eine im Speicherkontext des Programms initiierte Variable ändern, wenn Sie im Ausführungsstapel einen Verweis darauf eingebettet haben. Das ist das implizite Prinzip, das von der Von Neumann Architektur stammt. Dies unterscheidet sich von einer funktionalen Sprache, die verschiedene Ausführungskontexte ineinander einschließen sollte, die sich subtil unterscheiden und sicherstellen, dass ein von einer Variablen referenzierter Wert nicht durch eine spätere Zuweisung der Variablen modifiziert wird. Aber das ist eine andere Geschichte.