Wie schreibe ich Code in Java 11, aber ziele auf Java 8 und höher ab?

Lesezeit: 7 Minuten

Benutzeravatar von neshkeev
Neschkejew

Ich arbeite an einer kleinen Bibliothek und möchte aus offensichtlichen Gründen Code schreiben, der alle Java 11-Funktionen verwendet (mit Ausnahme von Modulen, die ich für den Moment annehme), aber ich möchte, dass die Bibliothek mit Java 8 und höher kompatibel ist.

Wenn ich das versuche:

javac -source 11 -target 1.8 App.java

Ich bekomme folgende Meldung:

warning: source release 11 requires target release 11

… und wenn ich mir den Bytecode ansehe, sehe ich, dass die Version der Klasse ist 0x37 (Java11):

$ xxd App.class
00000000: cafe babe 0000 0037 ...

Und Java 8 kann es nicht laden:

Exception in thread "main" java.lang.UnsupportedClassVersionError: App has been
    compiled by a more recent version of the Java Runtime (class file version 55.0),
    this version of the Java Runtime only recognizes class file versions up to 52.0
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
    at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
    at java.net.URLClassLoader.defineClass(URLClassLoader.java:468)
    at java.net.URLClassLoader.access$100(URLClassLoader.java:74)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:369)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:363)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:362)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:495)

Wie stellen Menschen eine solche Kompatibilität bereit? Ich bin offen für alle Build-Tools.

Für mich scheint es einfach zu sein, Hochsprache (Java) in Niedrigsprache (Bytecode) umzuwandeln. Es scheint mir, dass, wenn sich die Hochsprache ändert, die Low-Level gleich bleiben sollte. Deshalb hielt ich es für möglich.

AKTUALISIEREN

Leute, ich glaube nicht, dass diese Antwort Move to OpenJDK-11 dupliziert, sondern in Java 8 kompiliert, weil das OP dort fragt, wie der Code weiter produziert werden soll Java 8 Funktionen, sondern zielen auf Java 11 ab (was nur eine berühmte Abwärtskompatibilität ist). Meine Frage ist umgekehrt: Ich möchte den Code in Java 11 erstellen, aber Java 8 als Ziel. Ich bin auf diese Frage gestoßen, als ich das Thema recherchiert habe, bevor ich die Frage gestellt habe. Ich fand es nicht anwendbar auf meine Situation.

Die andere Frage, ob Java 8-Code für die Ausführung auf Java 7 JVM kompiliert werden kann, sieht meiner Frage ähnlich, wurde jedoch 2013 gestellt, und der Bytecode wurde offensichtlich zwischen Java 7 und Java 8 geändert.

Ich dachte nicht, dass sich der Bytecode seit Java 8 so sehr geändert hat, deshalb habe ich diese Frage gestellt.

  • Wenn Sie eine Java 8-Version Ihres Projekts als Ziel verwenden und erstellen möchten, können Sie nur Java 8-Sprachfunktionen und -Bibliotheken verwenden, nicht die neueren.

    – Nikolaus Hirras

    30. Januar 2019 um 19:04 Uhr

  • Welche Java 11-Sprachfunktionen benötigen Sie? Die Unterschiede zwischen Java 8 und Java 11 sind gering.

    – ZhekaKozlov

    31. Januar 2019 um 12:42 Uhr


  • Ich denke, der einzige (sichere) Weg, dies zu tun, besteht darin, zu Kotlin zu wechseln, das JVM 1.8-kompatiblen Bytecode erzeugen kann. Auf diese Weise erhalten Sie Zugriff auf moderne Sprachfunktionen, erzeugen aber kompatiblen Bytecode. Java 11-Sprachfunktionen erfordern ein neues Bytecode-Format, das von JVM 1.8 nicht unterstützt wird.

    – beresowskij

    16. April um 21:04 Uhr


Benutzeravatar von Holger
Holger

Die Konvertierung von für JDK 11 kompilierten Klassen in JDK 8 wäre zwar mit einem ausgefeilten Tool theoretisch möglich, aber nicht trivial. Es gibt signifikante Änderungen auf der binären Ebene.

Zuerst wurde JDK 11 eingeführt Nest -Typen, sodass beim Zugriff keine synthetischen Zugriffsmethoden generiert werden müssen private Angehörige innerer/äußerer Klassen. Natürlich würde ein solcher Zugriff in älteren Versionen fehlschlagen.

Es wurde auch eingeführt dynamische Konstanten, obwohl ich nicht weiß, ob die Java-Sprache diese Funktion irgendwo ausnutzt. Dies ist hauptsächlich für zukünftige Versionen gedacht.

Dann wird seit JDK 9 die Zeichenfolgenverkettung mit kompiliert invokedynamic in Bezug auf java.lang.invoke.StringConcatFactory was in Java 8 nicht vorhanden ist.

Eine Funktion, die funktionieren könnte, ist private Methoden in Schnittstellen, die in Java 9 als Sprachfeature eingeführt, aber in Java 8 bereits auf der Binärebene behandelt werden.

Java 8 wäre auch nicht in der Lage, Moduldefinitionen zu verarbeiten, aber ich nehme an, sie würden ignoriert.

Buurmans Benutzeravatar
Burman

Nein, Sie können den Java 11-Quellcode nicht in Java 8-Binärdateien kompilieren.

Im javac Begriffe, die -source Der Parameter darf nicht größer sein als der -target Parameter.

Wenn Sie also Java 8-Binärdateien erstellen möchten, sollten Ihre Quellen in Java 8 (oder früher) geschrieben sein. Wenn Sie keine Java 11-Sprachfunktionen verwenden, befinden sich Ihre Quellen im Grunde bereits in Java 8, sodass dies kein allzu großes Problem darstellen sollte.

Beachten Sie, dass Sie weiterhin ein JDK 11 verwenden können, um die Java 8-Quelle in Java 8-Binärdateien zu kompilieren. Die JDK-Version kann größer sein als die Quell- und/oder Zielversionen.

Hinweis: Die Javac-Dokumentation sagt nichts über die -source Parameter muss kleiner oder gleich dem sein -target Parameter. Es gibt jedoch viele inoffizielle Dokumentationen. Zum Beispiel https://stackoverflow.com/a/9261298/691074

Soweit ich das beurteilen kann, gibt es auch kein einziges Gegenbeispiel, um eine solche Situation tatsächlich zum Laufen zu bringen.

  • Bitte geben Sie mir den Link zu Dokumenten, die diese Aussage unterstützen “In javac Begriffe, die -source Der Parameter darf nicht größer sein als der -targetParameter.” Ich habe nichts dergleichen gefunden hier

    – Neschkejew

    9. Mai 2019 um 12:24 Uhr

  • Sie haben Recht, dass die Dokumente das nicht erwähnen. Die beste Ressource, die ich finden konnte, besagt, dass laut Dokumentation der Compiler sollte in der Lage sein, dies zu tun. In der Praxis scheint dies jedoch nicht der Fall zu sein. Ich werde die beste Quelle hinzufügen, die ich kurzfristig finden konnte.

    – Burman

    9. Mai 2019 um 13:54 Uhr

Es gibt ein Tool namens Jabel erstellt von @bsideup, mit dem Sie dies tun können. Es gibt vor, ein Anmerkungsprozessor zu sein, damit es in Javac eingesteckt werden kann. Es führt jedoch keine eigentliche Anmerkungsverarbeitung durch. Stattdessen hackt es sich während der Kompilierung in Javac-Interna und lässt es glauben, dass neuere Funktionen wie VarDiamant in der anonymen Klassenerstellung und sogar neuere wie Textblöcke und Ausdrücke wechseln können in Java 8 verwendet werden.

Dies ist eine Hacky-Lösung ohne Garantie, also mit Vorsicht verwenden.

Benutzeravatar von GhostCat
Geisterkatze

Ich könnte mich hier irren, aber zumindest bis jetzt ist Javac nicht dazu gedacht, auf diese Weise verwendet zu werden.

Ein bisschen raten hier: Sie könnten versuchen zu sehen, ob --release 8 --target 8 funktioniert (ohne Angabe der --source 11 Parameter).

Aber ich bezweifle, dass das funktionieren wird. Ich denke, es gibt keine Unterstützung in Javac, um N-Quellcodefunktionen zu akzeptieren und diese rückwärts in frühere Zielversionen zu kompilieren.

Natürlich könnte der Compiler Kenntnis über die erforderlichen Transformationen haben, um N Quellcode in (Nm) Byte-Code umzuwandeln. Aber das würde den Compiler viel komplexer machen, und jede Version würde dazu beitragen. Es würde auch dramatische Kosten für die hinzufügen testen Bemühungen. Ich bezweifle, dass die Compiler-Betreuer bereit sind, sich darauf einzulassen. Es ist wirklich nicht so, dass dies ein weit verbreiteter Anwendungsfall ist.

Also, die nur “Lösung” weiß ich: Verzweigung und doppelte Wartung. Und um die Dinge vernünftig zu halten, würde ich einfach eine Java 8-Version behalten, und kann sein eine für Java 11.

Obwohl ich nichts explizites darin gesehen habe javadoc Für Javac können Sie meines Erachtens nur dieselbe Version für die Optionen -source und -target angeben. Java 11-Funktionen werden in Java 8 nicht unterstützt, obwohl das Gegenteil der Fall ist, eine höhere Java-Version kann Code ausführen, der in einer niedrigeren Version kompiliert wurde. Daher glaube ich nicht, dass es möglich ist, in Java 11 geschriebenen Code so zu kompilieren, dass er in Java 8 ausgeführt werden kann.

  • Ich denke: es ist mit den Tools, die wir jetzt haben, (höchstwahrscheinlich) nicht möglich. Wenn Sie genügend Ressourcen aufwenden, könnten Sie wahrscheinlich einen solchen Compiler erstellen. Es ist nur so, dass dies sehr teuer sein kann und nicht viele Leute an dieser Funktion interessiert sind.

    – Geisterkatze

    30. Januar 2019 um 19:44 Uhr

  • @GhostCat Es gibt große Unterschiede im Klassendateiformat. Z.B KONSTANT_Dynamisch und angegebene Zeichenfolgenverkettung. Ich habe keine Ahnung, wie diese in Java 8 übersetzt werden können …

    – ZhekaKozlov

    31. Januar 2019 um 12:46 Uhr

  • Ich denke: es ist mit den Tools, die wir jetzt haben, (höchstwahrscheinlich) nicht möglich. Wenn Sie genügend Ressourcen aufwenden, könnten Sie wahrscheinlich einen solchen Compiler erstellen. Es ist nur so, dass dies sehr teuer sein kann und nicht viele Leute an dieser Funktion interessiert sind.

    – Geisterkatze

    30. Januar 2019 um 19:44 Uhr

  • @GhostCat Es gibt große Unterschiede im Klassendateiformat. Z.B KONSTANT_Dynamisch und angegebene Zeichenfolgenverkettung. Ich habe keine Ahnung, wie diese in Java 8 übersetzt werden können …

    – ZhekaKozlov

    31. Januar 2019 um 12:46 Uhr

1431120cookie-checkWie schreibe ich Code in Java 11, aber ziele auf Java 8 und höher ab?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy