¿Se puede compilar el código Java 8 para que se ejecute en Java 7 JVM?

163

Java 8 presenta nuevas características importantes del lenguaje, como las expresiones lambda.

¿Estos cambios en el lenguaje van acompañados de cambios tan significativos en el código de bytes compilado que evitarían que se ejecute en una máquina virtual Java 7 sin utilizar algún retrotranslator?

Nicola Ambrosetti
fuente
posible duplicado de ¿Hay ejemplos específicos de incompatibilidades hacia atrás entre las versiones de Java?
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Respuestas:

146

No, el uso de las funciones 1.8 en su código fuente requiere que apunte a una VM 1.8. Acabo de probar la nueva versión de Java 8 e intenté compilar -target 1.7 -source 1.8, y el compilador se niega:

$ javac Test -source 1.8 -target 1.7
javac: source release 1.8 requires target release 1.8
JesperE
fuente
44
No, no creo que lo haga. Java tiene una pequeña participación en el mercado de computadoras de escritorio, pero mantiene esa pequeña participación en un control bastante estricto. Pero sí obstaculiza la adopción de nuevas versiones y características. No podré usar las características de Java 8 en el código que escribo durante bastante tiempo, ya que quiero evitar que las personas tengan que actualizar su instalación local de Java.
JesperE
¿Por qué? "Sí" implicaría que Java 8 se puede compilar para ejecutarse en una VM Java 7, lo cual es incorrecto según el compilador de Java 8.
JesperE
55
Ahora veo: su "No" responde el título de la pregunta, no el cuerpo de la pregunta.
Abdull
58

Los métodos predeterminados requieren tales cambios en el bytecode y la JVM que hubieran sido imposibles de hacer en Java 7. El verificador de bytecode de Java 7 y versiones posteriores rechazará las interfaces con los cuerpos de los métodos (excepto el método de inicializador estático). Intentar emular métodos predeterminados con métodos estáticos en el lado de la persona que llama no produciría los mismos resultados, porque los métodos predeterminados pueden anularse en subclases. Retrolambda tiene un soporte limitado para los métodos predeterminados de backport, pero nunca puede ser completamente backport porque realmente requiere nuevas características de JVM.

Lambdas podría ejecutarse en Java 7 tal cual, si las clases de API necesarias simplemente existieran allí. La instrucción invocada dinámica existe en Java 7, pero habría sido posible implementar lambdas para que genere las clases lambda en tiempo de compilación (las primeras compilaciones de JDK 8 lo hicieron de esa manera) en cuyo caso funcionaría en cualquier versión de Java. (Oracle decidió usar invocados dinámicos para lambdas para pruebas futuras; tal vez algún día JVM tendrá funciones de primera clase, por lo que puede invocarse dinámico para usarlos en lugar de generar una clase para cada lambda, mejorando así el rendimiento). Lo que hace Retrolambda es que procesa todas esas instrucciones dinámicas invocadas y las reemplaza con clases anónimas; lo mismo que hace Java 8 en tiempo de ejecución cuando una lamdba invocada dinámicamente se llama por primera vez.

Repetir anotaciones es solo azúcar sintáctico. Son bytecode compatibles con versiones anteriores. En Java 7 solo necesitaría implementar los métodos de ayuda (por ejemplo, getAnnotationsByType ) que ocultan los detalles de implementación de una anotación de contenedor que contiene las anotaciones repetidas.

AFAIK, las anotaciones de tipo solo existen en el momento de la compilación, por lo que no deberían requerir cambios de bytecode, por lo que solo cambiar el número de versión de bytecode de las clases compiladas de Java 8 debería ser suficiente para que funcionen en Java 7.

Los nombres de los parámetros del método existen en el bytecode con Java 7, por lo que también es compatible. Puede obtener acceso a ellos leyendo el código de bytes del método y mirando los nombres de las variables locales en la información de depuración del método. Por ejemplo, Spring Framework hace exactamente eso para implementar @PathVariable , por lo que probablemente haya un método de biblioteca al que podría llamar. Debido a que los métodos de interfaz abstractos no tienen un cuerpo de método, esa información de depuración no existe para los métodos de interfaz en Java 7, y AFAIK tampoco en Java 8.

Las otras características nuevas son en su mayoría API nuevas, mejoras en HotSpot y herramientas. Algunas de las nuevas API están disponibles como bibliotecas de terceros (p. Ej. ThreeTen-Backport y streamsupport ).

En resumen, los métodos predeterminados requieren nuevas funciones de JVM, pero las otras funciones del lenguaje no. Si desea usarlos, deberá compilar el código en Java 8 y luego transformar el bytecode con Retrolambda al formato Java 5/6/7 . Como mínimo, la versión de bytecode debe cambiarse, y javac no -source 1.8 -target 1.7lo permite, por lo que se requiere un retrotranslator.

Esko Luontola
fuente
3
En realidad, las anotaciones de tipo pueden ser visibles en tiempo de ejecución. stackoverflow.com/questions/22374612/…
Antimony
33

Hasta donde yo sé, ninguno de estos cambios en JDK 8 requirió la adición de nuevos códigos de bytes. Parte de la instrumentación lambda se realiza utilizandoinvokeDynamic (que ya existe en JDK 7). Entonces, desde el punto de vista del conjunto de instrucciones JVM, nada debería hacer que la base de código sea incompatible. Sin embargo, hay muchas mejoras de compilación y API asociadas que podrían hacer que el código de JDK 8 sea difícil de compilar / ejecutar en JDK anteriores (pero no lo he intentado).

Tal vez el siguiente material de referencia pueda ayudar de alguna manera a enriquecer la comprensión de cómo se están instrumentando los cambios relacionados con lambda.

Estos explican en detalle cómo se instrumentan las cosas debajo del capó. Quizás pueda encontrar la respuesta a sus preguntas allí.

Edwin Dalorzo
fuente
77
No hay nuevos códigos de bytes, sino nuevas estructuras. El verificador vomitará.
Jonathan S. Fisher
12
Un buen ejemplo son las interfaces. Ahora pueden contener métodos. El verificador Java7 no está equipado para manejar esto. Se utilizan todos los códigos de bytes antiguos, pero de una manera nueva.
Jonathan S. Fisher
1
Me pregunto cómo puede el compilador scala con tantas características de lenguaje lograr una versión jvm objetivo de incluso jdk5.
Marinos un
1
@MarinosAn ¿Qué quieres decir exactamente? MI con rasgos que contienen métodos concretos, p. Ej.class C extends A with B , se lleva a cabo con interfaces normales Ay By clases de compañía A$classy B$class. La clase Csimplemente reenvía los métodos a las clases complementarias estáticas. Los auto-tipos no se aplican en absoluto, las lambdas se trasladan en tiempo de compilación a clases internas abstractas, por lo que es una new D with A with Bexpresión. La coincidencia de patrones es un conjunto de estructuras if-else. Devoluciones no locales? mecanismo try-catch de la lambda. ¿Queda algo? (Curiosamente, mi scalac dice que 1.6 es el predeterminado)
Adowrath
1
Por supuesto, los autotipos, etc., están codificados en atributos y anotaciones de clase especiales para que scalac pueda usar y hacer cumplir las reglas al usar clases ya compiladas.
Adowrath
-5

Puede hacerlo, -source 1.7 -target 1.7entonces se compilará. Pero no se compilará si tiene características específicas de Java 8 como lambdas

kalgecin
fuente
La pregunta plantea explícitamente el uso de las nuevas características del lenguaje, por -source 1.7lo que no volará.
toolforger