web-dev-qa-db-fra.com

Le code Java 8 peut-il être compilé pour s'exécuter sur JVM Java 7?

Java 8 introduit de nouvelles fonctionnalités importantes telles que les expressions lambda.

Ces changements de langage sont-ils accompagnés de changements aussi importants dans le bytecode compilé qui empêcheraient son exécution sur une machine virtuelle Java 7 sans utiliser de retrotranslator?

159
Nicola Ambrosetti

Non, l'utilisation de fonctionnalités 1.8 dans votre code source nécessite de cibler une machine virtuelle 1.8. Je viens d'essayer la nouvelle version de Java 8 et d'essayer 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
137
JesperE

Les méthodes par défaut requièrent de tels changements dans le bytecode et la JVM qu’ils auraient été impossibles à faire sur Java 7. Le vérificateur de bytecode de Java 7 et inférieur rejettera les interfaces avec des corps de méthodes ( sauf pour la méthode d'initialisation statique). Essayer d'émuler des méthodes par défaut avec des méthodes statiques côté appelant ne produirait pas les mêmes résultats, car les méthodes par défaut peuvent être remplacées dans des sous-classes. Retrolambda prend en charge de manière limitée les méthodes par défaut de rétroportage, mais cette dernière ne peut jamais être entièrement rétroportée, car elle nécessite réellement de nouvelles fonctionnalités JVM.

Lambdas pourrait s’exécuter sur Java 7 tel quel, si les classes d’API nécessaires n’existaient que là. L'instruction invokedynamic existe sur Java 7, mais il aurait été possible d'implémenter lambdas de manière à générer les classes lambda au moment de la compilation (les premières versions de JDK 8 le faisaient ainsi), auquel cas cela fonctionnerait. toute version Java. (Oracle a décidé d'utiliser invokedynamic pour les lambdas pour une mise à jour future. Peut-être qu'un jour la machine virtuelle Java aura des fonctions de première classe; invokedynamic peut donc être modifié pour les utiliser au lieu de générer une classe pour chaque lambda, améliorant ainsi les performances.) Retrolambda qu'il traite toutes ces instructions invokedynamic et les remplace par des classes anonymes; La même chose que ce que Java 8 fait à l'exécution lorsqu'un lamdba invokedynamic est appelé pour la première fois.

Annotations à Répéter n'est qu'un sucre syntaxique. Ils sont compatibles avec le bytecode avec les versions précédentes. Dans Java 7, il vous suffira d'implémenter vous-même les méthodes d'assistance (par exemple, getAnnotationsByType ) qui masquent les détails d'implémentation d'une annotation de conteneur contenant les annotations répétées.

D'après les informations dont je dispose, Les annotations de type n'existent qu'au moment de la compilation. Elles ne devraient donc pas nécessiter de modifications de code-octet. Il suffit donc de modifier le numéro de version de code-octet de la classe Java -8 compilée. les travaillent 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 consultant les noms de variables locales dans les informations de débogage de la méthode. Par exemple, Spring Framework fait exactement cela pour implémenter @ PathVariable , il existe donc probablement une méthode de bibliothèque que vous pouvez appeler. Les méthodes d'interface abstraites n'ayant pas de corps de méthode, ces informations de débogage n'existent pas pour les méthodes d'interface dans Java 7 et AFAIK non plus sur Java 8.

Les autres nouvelles fonctionnalités sont principalement de nouvelles API, des améliorations de HotSpot et de l'outillage. Certaines des nouvelles API sont disponibles en tant que bibliothèques tierces (par exemple, ThreeTen-Backport et streamsupport ).

En résumé, les méthodes par défaut nécessitent de nouvelles fonctionnalités JVM, contrairement aux autres fonctionnalités de langage. Si vous souhaitez les utiliser, vous devez compiler le code dans Java 8, puis transformer le bytecode avec Retrolambda en Java 5/6/Format 7. Au minimum, la version du bytecode doit être modifiée et javac interdit -source 1.8 -target 1.7 donc un rétrotranslateur est requis.

55
Esko Luontola

Pour autant que je sache, aucun de ces changements dans JDK 8 n’a nécessité l’ajout de nouveaux codes bytec. Une partie de l'instrumentation lambda est réalisée à l'aide de invokeDynamic (qui existe déjà dans JDK 7). Donc, du point de vue du jeu d’instructions JVM, rien ne devrait rendre la base de code incompatible. Cependant, il existe de nombreuses améliorations associées aux API et au compilateur qui pourraient rendre le code de JDK 8 difficile à compiler/exécuter avec les JDK 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 manière ou d’une autre à enrichir la compréhension de la façon dont les changements liés au lambda sont instrumentés.

Celles-ci expliquent en détail comment les choses sont instrumentées sous le capot. Peut-être que vous pouvez trouver la réponse à vos questions ici.

31
Edwin Dalorzo

Si vous souhaitez utiliser un "rétrotranslateur", essayez l'excellent Retrolambda d'Esko Luontola: https://github.com/orfjackal/retrolambda

11
Stefan Zobel