web-dev-qa-db-fra.com

Comment produire du code en Java 11, mais cibler Java 8 et supérieur?)

Je travaille sur une petite bibliothèque et pour des raisons évidentes, je voudrais produire du code en utilisant toutes les fonctionnalités de Java 11 (sauf les modules je suppose pour l'instant), mais je voudrais que la bibliothèque soit compatible avec Java 8 et supérieur.

Quand j'essaye ceci:

javac -source 11 -target 1.8 App.Java

Je reçois le message suivant:

warning: source release 11 requires target release 11

... et quand je regarde le code d'octet, je vois que la version de la classe est 0x37 (Java 11):

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

Et Java 8 ne peut pas le charger:

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)

Comment les gens offrent-ils une telle compatibilité? Je suis ouvert à tous les outils de build.

Pour moi, il semble facile de transformer simplement un langage de haut niveau (Java) en bas niveau (bytecode). Il me semble que lorsque le langage de haut niveau change, le bas niveau doit rester le même. C'est pourquoi j'ai pensé que c'était possible.

[~ # ~] mise à jour [~ # ~]

Les gars, je ne pense pas que cette réponse duplique Déplacer vers OpenJDK-11 mais compiler en Java 8 , car là l'OP demande comment continuer à produire le code avec Java 8, Mais cible Java 11 (qui est juste une célèbre compatibilité descendante). Ma question est dans l'autre sens: je veux produire le code en Java 11, mais cible Java 8. Je suis tombé sur cette question lorsque je faisais des recherches sur le sujet avant de poser la question . Je ne l'ai pas trouvé applicable à ma situation.

L'autre question Can Java 8 code peut être compilé pour fonctionner sur Java 7 JVM ressemble à ma question, mais elle a été posée en 2013 et le bytecode évidemment changé entre Java 7 et Java 8.

Je ne pensais pas que le bytecode avait tellement changé depuis Java 8 c'est pourquoi j'ai posé cette question.

22
neshkeev

Bien que la conversion des classes compilées pour JDK 11 en JDK 8 soit théoriquement possible avec un outil sophistiqué, ce n'est pas anodin. Il y a des changements importants au niveau binaire.

Tout d'abord, JDK 11 a introduit les types nest , ce qui élimine la nécessité de générer des méthodes d'accesseur synthétiques lors de l'accès aux membres private des classes internes/externes. Bien sûr, cet accès échouerait dans les anciennes versions.

Il a également introduit constantes dynamiques , même si je ne sais pas si le langage Java exploite cette fonctionnalité n'importe où. Ceci est principalement destiné aux versions futures.

Puis, depuis JDK 9, la concaténation de chaînes est compilée à l'aide de invokedynamic faisant référence à Java.lang.invoke.StringConcatFactory qui n'est pas présent dans Java 8.

Une fonctionnalité qui pourrait fonctionner, ce sont les méthodes private dans les interfaces, introduites dans Java 9 en tant que fonctionnalité de langage, mais déjà gérées au niveau binaire dans Java 8.

Java 8 serait également incapable de traiter les définitions de module, mais je suppose qu'elles seraient ignorées.

13
Holger

Bien que je n'aie rien vu d'explicite dans javadoc pour javac, je pense que vous ne pouvez indiquer la même version que pour les options -source et -target. Java 11 fonctionnalités ne sont pas prises en charge dans Java 8 bien que l'inverse soit vrai, une version supérieure Java version peut exécuter du code compilé) dans une version inférieure. Je ne pense donc pas qu'il soit possible de compiler du code écrit en Java 11 pour être exécutable en Java 8.

6
Abra

Je peux me tromper ici, mais au moins jusqu'à présent, javac n'est pas destiné à être utilisé de cette façon.

Un peu de devinettes ici: vous pourriez essayer de voir si --release 8 --target 8 fonctionne (sans donner le --source 11 paramètre).

Mais je doute que cela fonctionne. Je pense qu'il n'y a pas de support dans javac pour accepter les fonctionnalités de code source N, et les avoir compilées à l'envers vers les versions cibles antérieures.

Bien sûr, le compilateur pourrait avoir des connaissances sur les transformations requises pour transformer N code source en (N-m) octet code. Mais cela rendrait le compilateur beaucoup plus complexe, et chaque version ajouterait à cela. Cela ajouterait également un coût dramatique aux efforts de testing. Je doute que les mainteneurs du compilateur soient prêts à accepter cela. Ce n'est vraiment pas comme si c'était un cas d'utilisation répandu.

Donc, la seulement "solution" que je connais: ramification et double maintenance. Et pour garder les choses raisonnables, je garderais simplement une Java 8, et peut-être une pour Java 11.

Non, vous ne pouvez pas compiler Java 11 source vers Java 8 binaires.

En javac, le -source le paramètre ne peut pas être supérieur au -target paramètre.

Donc, si vous voulez produire Java 8 binaires, vos sources doivent être écrites en Java 8 (ou plus tôt). Si vous n'utilisez aucun = Java 11 fonctionnalités du langage, vos sources sont déjà en fait Java 8 donc ce ne devrait pas être un problème trop important).

Notez que vous pouvez toujours utiliser un JDK 11 pour compiler la source Java 8 en Java 8. La version JDK peut être supérieur aux versions source et/ou cible.

Remarque: la documentation javac ne dit rien sur le -source paramètre devant être inférieur ou égal à -target paramètre. Il y a cependant beaucoup de documentation non officielle. Par exemple, https://stackoverflow.com/a/9261298/691074

Autant que je sache, il n'y a pas non plus un seul contre-exemple pour faire fonctionner une telle situation.

5
Buurman

Il existe un outil appelé Jabel créé par @ bsideup , qui vous permet de le faire. Il prétend être un processeur d'annotation et peut donc être connecté à javac. Cependant, il ne fait aucun traitement d'annotation réel. Au lieu de cela, pendant la compilation, il pirate les internes javac et fait croire que les nouvelles fonctionnalités comme var , le diamant dans la création de classes anonymes et même les plus récentes comme blocs de texte et - passer des expressions sont utilisables dans Java 8.

Il s'agit d'une solution hacky sans aucune garantie, utilisez-la donc avec prudence.

3
Tagir Valeev