Java 8 führt wichtige neue Sprachfunktionen wie Lambda-Ausdrücke ein.
Gehen diese Änderungen in der Sprache mit solchen signifikanten Änderungen im kompilierten Bytecode einher, die verhindern würden, dass er auf einer virtuellen Java 7-Maschine ohne Verwendung eines Retrotranslators ausgeführt wird?
Antworten:
Nein, für die Verwendung von 1.8-Funktionen in Ihrem Quellcode müssen Sie eine 1.8-VM als Ziel festlegen. Ich habe gerade die neue Java 8-Version ausprobiert und versucht, mit zu kompilieren
-target 1.7 -source 1.8
, und der Compiler lehnt ab:quelle
Standardmethoden erfordern solche Änderungen am Bytecode und an der JVM, die unter Java 7 nicht möglich gewesen wären. Der Bytecode-Verifizierer von Java 7 und niedriger lehnt Schnittstellen mit Methodenkörpern ab (mit Ausnahme der statischen Initialisierungsmethode). Der Versuch, Standardmethoden mit statischen Methoden auf der Aufruferseite zu emulieren, führt nicht zu denselben Ergebnissen, da Standardmethoden in Unterklassen überschrieben werden können. Retrolambda bietet nur eingeschränkte Unterstützung für das Backportieren von Standardmethoden, kann jedoch niemals vollständig zurückportiert werden, da wirklich neue JVM-Funktionen erforderlich sind.
Lambdas könnte unter Java 7 unverändert ausgeführt werden, wenn die erforderlichen API-Klassen nur dort vorhanden wären. Die aufgerufene dynamische Anweisung existiert unter Java 7, aber es wäre möglich gewesen, Lambdas so zu implementieren, dass sie die Lambda-Klassen zur Kompilierungszeit generiert (frühe JDK 8-Builds haben dies so gemacht). In diesem Fall würde sie auf jeder Java-Version funktionieren. (Oracle hat beschlossen, invokedynamic für Lambdas für die Zukunftssicherung zu verwenden. Vielleicht wird JVM eines Tages über erstklassige Funktionen verfügen, sodass invokedynamic geändert werden kann, um sie zu verwenden, anstatt für jedes Lambda eine Klasse zu generieren, wodurch die Leistung verbessert wird.) Retrolambda verbessert dies dass es alle diese aufgerufenen dynamischen Anweisungen verarbeitet und durch anonyme Klassen ersetzt; Das gleiche wie Java 8 zur Laufzeit, wenn eine Lamdba-Aufrufdynamik zum ersten Mal aufgerufen wird.
Das Wiederholen von Anmerkungen ist nur syntaktischer Zucker. Sie sind mit früheren Versionen Bytecode-kompatibel. In Java 7 müssten Sie nur die Hilfsmethoden (z. B. getAnnotationsByType ) selbst implementieren, die die Implementierungsdetails einer Container-Annotation verbergen, die die wiederholten Annotationen enthält.
AFAIK, Typanmerkungen sind nur zur Kompilierungszeit vorhanden, daher sollten keine Bytecode-Änderungen erforderlich sein. Daher sollte es ausreichen, nur die Bytecode-Versionsnummer der Java 8-kompilierten Klassen zu ändern, damit sie unter Java 7 funktionieren.
Methodenparameternamen sind im Bytecode mit Java 7 vorhanden, daher ist dies auch kompatibel. Sie können auf sie zugreifen, indem Sie den Bytecode der Methode lesen und die lokalen Variablennamen in den Debug-Informationen der Methode anzeigen. Zum Beispiel macht das Spring Framework genau das, um @PathVariable zu implementieren , also gibt es wahrscheinlich eine Bibliotheksmethode, die Sie aufrufen könnten. Da abstrakte Schnittstellenmethoden keinen Methodenkörper haben, sind diese Debug-Informationen für Schnittstellenmethoden in Java 7 und AFAIK auch nicht in Java 8 vorhanden.
Die anderen neuen Funktionen sind hauptsächlich neue APIs, Verbesserungen an HotSpot und Tools. Einige der neuen APIs sind als Bibliotheken von Drittanbietern verfügbar (z. B. ThreeTen-Backport und Streamsupport ).
Zusammenfassend lässt sich sagen, dass Standardmethoden neue JVM-Funktionen erfordern, die anderen Sprachfunktionen jedoch nicht. Wenn Sie sie verwenden möchten, müssen Sie den Code in Java 8 kompilieren und dann den Bytecode mit Retrolambda in das Java 5/ 6/ 7-Format umwandeln. Zumindest muss die Bytecode-Version geändert werden, und Javac lässt dies nicht zu,
-source 1.8 -target 1.7
sodass ein Retrotranslator erforderlich ist.quelle
Soweit ich weiß, erforderte keine dieser Änderungen in JDK 8 das Hinzufügen neuer Bytecodes. Ein Teil der Lambda-Instrumentierung wird mit
invokeDynamic
(die bereits in JDK 7 vorhanden sind) durchgeführt. Aus Sicht des JVM-Befehlssatzes sollte also nichts die Codebasis inkompatibel machen. Es gibt jedoch viele API-assoziierte und Compiler-Verbesserungen, die es schwierig machen könnten, den Code aus JDK 8 unter früheren JDKs zu kompilieren / auszuführen (aber ich habe dies nicht versucht).Vielleicht kann das folgende Referenzmaterial irgendwie dazu beitragen, das Verständnis dafür zu verbessern, wie die Änderungen in Bezug auf Lambda instrumentiert werden.
Diese erklären im Detail, wie Dinge unter der Haube instrumentiert werden. Vielleicht finden Sie dort die Antwort auf Ihre Fragen.
quelle
class C extends A with B
, ist mit normalen Schnittstellen implementiertA
undB
und Begleiter KlassenA$class
undB$class
. KlasseC
leitet die Methoden einfach an die statischen Begleitklassen weiter. Selbsttypen werden überhaupt nicht erzwungen, Lambdas werden zur Kompilierungszeit in abstrakte innere Klassen transpiliert, ebenso einnew D with A with B
Ausdruck. Pattern-Matching ist eine Reihe von If-else-Strukturen. Nicht lokale Rückgabe? Try-Catch-Mechanismus aus dem Lambda. Etwas übrig? (Interessanterweise sagt mein Scalac, dass 1.6 die Standardeinstellung ist)Wenn Sie bereit sind, einen "Retrotranslator" zu verwenden, probieren Sie Esko Luontolas ausgezeichnetes Retrolambda: https://github.com/orfjackal/retrolambda
quelle
Sie können
-source 1.7 -target 1.7
dann kompilieren. Es wird jedoch nicht kompiliert, wenn Sie Java 8-spezifische Funktionen wie Lambdas habenquelle
-source 1.7
wird also nicht fliegen.