Le code Java 8 peut-il être compilé pour fonctionner sur Java 7 JVM?

Java 8 introduit de nouvelles caractéristiques linguistiques importantes telles que les expressions lambda.

est-ce que ces changements dans le langage sont accompagnés de changements si significatifs dans le code compilé bytecode qui l'empêcheraient d'être exécuté sur une machine virtuelle Java 7 sans utiliser un rétrotranslateur?

152
demandé sur Arto Bendiken 2013-04-22 13:29:24

5 réponses

Non, l'utilisation des fonctionnalités 1.8 dans votre code source vous oblige à cibler une VM 1.8. Je viens d'essayer la nouvelle version de Java 8 et j'ai essayé de compiler avec -target 1.7 -source 1.8 , et le compilateur refuse:

$ javac Test -source 1.8 -target 1.7
javac: source release 1.8 requires target release 1.8
123
répondu JesperE 2014-03-18 22:30:24
Les méthodes par défaut

nécessitent de telles modifications au bytecode et au JVM qu'elles auraient été impossibles à faire sur Java 7. Le vérificateur de bytecode de Java 7 et inférieur rejettera les interfaces avec les corps de méthode (à l'exception de la méthode static initializer). Essayer d'émuler les méthodes par défaut avec des méthodes statiques du côté de l'appelant ne produirait pas les mêmes résultats, parce que les méthodes par défaut peuvent être supplantées dans les sous-classes. Retrolambda a un soutien limité pour backporting méthodes par défaut, mais il ne peut jamais être entièrement rétroporté car il nécessite vraiment de nouvelles fonctionnalités JVM.

Lambdas pourrait fonctionner sur Java 7 tel quel, Si les classes API nécessaires y existaient. L'instruction invokedynamic existe sur Java 7, mais il aurait été possible d'implémenter lambdas pour qu'il génère les classes lambda au moment de la compilation (les premières constructions JDK 8 l'ont fait de cette façon), auquel cas il fonctionnerait sur n'importe quelle version Java. (Oracle a décidé d'utiliser invokedynamic pour lambdas pour les épreuves futures; peut-être qu'un jour JVM aura des fonctions de première classe, alors invokedynamic peut être changé pour les utiliser au lieu de générer une classe pour chaque lambda, améliorant ainsi la performance. Ce que Retrolambda fait, c'est qu'il traite toutes ces instructions invokedynamiques et les remplace par des classes anonymes; c'est la même chose que Java 8 fait à runtime quand un lamdba invokedynamic est appelé la première fois.

répéter Les Annotations ne sont que du sucre syntaxique. Ils sont compatibles avec les versions précédentes. En Java 7, vous aurez juste besoin de mettre en œuvre vous-même les méthodes helper (par exemple getAnnotationsByType ) qui masquent le détail d'implémentation d'une annotation de conteneur qui contient les annotations répétées.

AFAIK, Type Annotations n'existent qu'au moment de la compilation, donc ils ne devraient pas nécessiter de changements de bytecode, donc juste changer le numéro de version du bytecode des classes compilées Java 8 devrait suffire à les faire fonctionner sur Java 7.

les noms de paramètres de méthode existent dans le bytecode avec Java 7, donc c'est aussi compatible. Vous pouvez y accéder en lisant le bytecode de la méthode et en regardant les noms des variables locales dans les informations de débogage de la méthode. Par exemple, le cadre de printemps fait exactement cela pour mettre en œuvre @PathVariable , donc il y a probablement une méthode de bibliothèque que vous pourriez appeler. Parce que les méthodes d'interface abstraites n'ont pas de corps de méthode, cette information de débogage n'existe pas pour les méthodes d'interface en Java 7, et AFAIK ni sur Java 8.

les autres nouvelles fonctionnalités sont principalement de nouveaux API, des améliorations de HotSpot et d'outillage. Certains des nouveaux API sont disponibles en tant que bibliothèques de tiers (par exemple ThreeTen-Backport et streamsupport ).

Summa summum, les méthodes par défaut nécessitent de nouvelles fonctionnalités JVM mais pas les autres. Si vous voulez les utiliser, vous devrez compiler le code en Java 8 puis transformer le bytecode avec Retrolambda en format Java 5/6/7. Au minimum la version bytecode doit être changée, et javac rejette -source 1.8 -target 1.7 donc un retrotranslateur est nécessaire.

53
répondu Esko Luontola 2015-11-03 00:16:28

pour autant que je sache, aucun de ces changements dans JDK 8 n'a nécessité l'ajout de nouveaux bytecodes. Une partie de l'instrumentation lambda est réalisée en utilisant invokeDynamic (qui existe déjà en JDK 7). Ainsi, du point de vue du jeu d'instructions JVM, rien ne devrait rendre la base de données incompatible. Il y a, cependant, beaucoup d'améliorations associées à L'API et au compilateur qui pourraient rendre le code de JDK 8 difficile à compiler/exécuter sous JDKs précédents (mais je n'ai pas essayé cela).

peut-être que le matériel de référence suivant peut aider d'une façon ou d'une autre à enrichir la compréhension de la façon dont les changements liés à lambda sont instrumentés.

ils expliquent en détail comment les choses sont instrumentés sous la hotte. Vous y trouverez peut-être la réponse à vos questions.

31
répondu Edwin Dalorzo 2017-09-07 11:17:50

si vous êtes prêt à utiliser un "retrotranslateur" essayez l'excellent Retrolambda D'Esko Luontola: https://github.com/orfjackal/retrolambda

11
répondu Stefan Zobel 2014-05-17 09:39:19

vous pouvez faire -source 1.7 -target 1.7 puis il compilera. Mais il ne sera pas compilé si vous avez java 8 fonctionnalités spécifiques comme lambdas

-5
répondu kalgecin 2014-12-20 15:33:38