Kann Java 8-Code für die Ausführung auf Java 7 JVM kompiliert werden?

163

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?

Nicola Ambrosetti
quelle
Mögliches Duplikat von Gibt es spezielle Beispiele für Abwärtsinkompatibilitäten zwischen Java-Versionen?
Ciro Santilli 4 冠状 病 六四 事件 4

Antworten:

146

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:

$ javac Test -source 1.8 -target 1.7
javac: source release 1.8 requires target release 1.8
JesperE
quelle
4
Nein, ich glaube nicht. Java hat einen kleinen Anteil am Desktop-Markt, hält diesen kleinen Anteil jedoch ziemlich fest im Griff. Dies behindert jedoch die Einführung neuer Versionen und Funktionen. Ich werde Java 8-Funktionen in dem Code, den ich schreibe, für einige Zeit nicht verwenden können, da ich vermeiden möchte, dass Benutzer ihre lokale Java-Installation aktualisieren müssen.
JesperE
Warum? "Ja" würde bedeuten, dass Java 8 für die Ausführung auf einer Java 7-VM kompiliert werden kann, was laut Java 8-Compiler falsch ist.
JesperE
5
Jetzt sehe ich: Ihr "Nein" beantwortet die Überschrift der Frage, nicht den Text der Frage.
Abdull
58

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.7sodass ein Retrotranslator erforderlich ist.

Esko Luontola
quelle
3
Tatsächlich können Typanmerkungen zur Laufzeit sichtbar sein. stackoverflow.com/questions/22374612/…
Antimon
33

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.

Edwin Dalorzo
quelle
7
Keine neuen Bytecodes, sondern neue Strukturen. Der Prüfer wird kotzen.
Jonathan S. Fisher
12
Ein gutes Beispiel sind Schnittstellen. Sie können jetzt Methoden enthalten. Der Java7-Verifizierer ist dafür nicht ausgestattet. Alle alten Bytecodes werden verwendet, jedoch auf neue Weise.
Jonathan S. Fisher
1
Ich frage mich, wie Scala Compiler mit so vielen Sprachfunktionen eine Ziel-JVM-Version von sogar JDK5 erreichen kann.
Marinos An
1
@MarinosAn Was meinst du genau? MI mit Eigenschaften , die konkreten Methoden enthalten, zum Beispiel class C extends A with B, ist mit normalen Schnittstellen implementiert Aund Bund Begleiter Klassen A$classund B$class. Klasse Cleitet die Methoden einfach an die statischen Begleitklassen weiter. Selbsttypen werden überhaupt nicht erzwungen, Lambdas werden zur Kompilierungszeit in abstrakte innere Klassen transpiliert, ebenso ein new D with A with BAusdruck. 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)
Adowrath
1
Selbstverständlich werden Selbsttypen usw. in speziellen Klassenattributen und Anmerkungen codiert, damit Scalac die Regeln verwenden und durchsetzen kann, wenn bereits kompilierte Klassen verwendet werden.
Adowrath
11

Wenn Sie bereit sind, einen "Retrotranslator" zu verwenden, probieren Sie Esko Luontolas ausgezeichnetes Retrolambda: https://github.com/orfjackal/retrolambda

Stefan Zobel
quelle
-5

Sie können -source 1.7 -target 1.7dann kompilieren. Es wird jedoch nicht kompiliert, wenn Sie Java 8-spezifische Funktionen wie Lambdas haben

Kalgecin
quelle
Die Frage setzt ausdrücklich die Verwendung der neuen Sprachfunktionen voraus, -source 1.7wird also nicht fliegen.
Werkzeugschmiede