O código Java 8 pode ser compilado para execução na Java 7 JVM?

163

O Java 8 apresenta novos recursos importantes da linguagem, como expressões lambda.

Essas mudanças na linguagem são acompanhadas por mudanças significativas no código de código compilado que impediriam que ela fosse executada em uma máquina virtual Java 7 sem usar algum retrotranslator?

Nicola Ambrosetti
fonte

Respostas:

146

Não, o uso dos recursos 1.8 no seu código-fonte exige que você direcione uma 1.8 VM. Eu apenas tentei a nova versão do Java 8 e tentei compilar -target 1.7 -source 1.8, e o compilador se recusa:

$ javac Test -source 1.8 -target 1.7
javac: source release 1.8 requires target release 1.8
JesperE
fonte
4
Não, acho que não. O Java possui uma pequena parcela do mercado de desktops, mas mantém essa pequena parcela em um controle bastante rígido. Mas isso dificulta a adoção de novas versões e recursos. Não poderei usar os recursos Java 8 no código que escrevo há algum tempo, pois quero evitar que as pessoas tenham que atualizar sua instalação Java local.
precisa saber é o seguinte
Por quê? "Sim" implicaria que o Java 8 pode ser compilado para executar em uma VM Java 7, o que está incorreto de acordo com o compilador Java 8.
precisa
5
Agora entendo: o seu "Não" responde ao título da pergunta, não ao corpo da pergunta.
Abdull 28/10
58

Os métodos padrão requerem essas alterações no bytecode e na JVM que seriam impossíveis de executar no Java 7. O verificador de bytecode do Java 7 e abaixo rejeitará as interfaces com os corpos do método (exceto o método inicializador estático). Tentar emular métodos padrão com métodos estáticos no lado do chamador não produziria os mesmos resultados, porque os métodos padrão podem ser substituídos nas subclasses. O Retrolambda possui suporte limitado para os métodos padrão de backport, mas nunca pode ser totalmente portado porque realmente requer novos recursos da JVM.

Os lambdas podem ser executados no Java 7 como estão, se as classes de API necessárias existirem lá. A instrução invokedynamic existe no Java 7, mas teria sido possível implementar lambdas para gerar as classes lambda em tempo de compilação (as primeiras compilações do JDK 8 fizeram dessa maneira) e, nesse caso, funcionaria em qualquer versão do Java. (A Oracle decidiu usar invokedynamic para lambdas para provas futuras; talvez um dia a JVM tenha funções de primeira classe, então o invokedynamic pode ser alterado para usá-las em vez de gerar uma classe para cada lambda, melhorando assim o desempenho.) O que a Retrolambda faz é que processa todas essas instruções dinâmicas invocadas e as substitui por classes anônimas; o mesmo que o Java 8 faz no tempo de execução quando um lamdba invokedynamic é chamado pela primeira vez.

Repetir anotações é apenas açúcar sintático. Eles são compatíveis com bytecode com versões anteriores. No Java 7, você só precisa implementar os métodos auxiliares (por exemplo, getAnnotationsByType ) que ocultam os detalhes de implementação de uma anotação de contêiner que contém as anotações repetidas.

AFAIK, anotações de tipo existem apenas no tempo de compilação, portanto, não devem exigir alterações de bytecode; portanto, basta alterar o número da versão do bytecode das classes compiladas Java 8 deve ser suficiente para fazê-las funcionar no Java 7.

Os nomes de parâmetro do método existem no bytecode com Java 7, portanto, isso também é compatível. Você pode obter acesso a eles lendo o bytecode do método e observando os nomes de variáveis ​​locais nas informações de depuração do método. Por exemplo, o Spring Framework faz exatamente isso para implementar @PathVariable , portanto, provavelmente existe um método de biblioteca que você poderia chamar. Como os métodos de interface abstrata não têm um corpo de método, essas informações de depuração não existem para os métodos de interface no Java 7 e o AFAIK nem no Java 8.

Os outros novos recursos são principalmente novas APIs, melhorias no HotSpot e ferramentas. Algumas das novas APIs estão disponíveis como bibliotecas de terceiros (por exemplo, ThreeTen-Backport e streamsupport ).

Resumo, métodos padrão requerem novos recursos da JVM, mas os outros recursos de idioma não. Se você quiser usá-los, precisará compilar o código no Java 8 e depois transformar o bytecode com Retrolambda para o formato Java 5/6/7. No mínimo, a versão do bytecode precisa ser alterada e o javac não permite -source 1.8 -target 1.7que seja necessário um retrotranslator.

Esko Luontola
fonte
3
Na verdade, as anotações de tipo podem ser visíveis em tempo de execução. stackoverflow.com/questions/22374612/…
Antimony
33

Até onde eu sei, nenhuma dessas alterações no JDK 8 exigiu a adição de novos bytecodes. Parte da instrumentação lambda está sendo feita usandoinvokeDynamic (que já existe no JDK 7). Portanto, do ponto de vista do conjunto de instruções da JVM, nada deve tornar a base de código incompatível. Existem, no entanto, muitas melhorias associadas à API e ao compilador que podem dificultar a compilação / execução do código do JDK 8 nos JDKs anteriores (mas ainda não tentei isso).

Talvez o seguinte material de referência possa ajudar de alguma forma a enriquecer o entendimento de como as mudanças relacionadas ao lambda estão sendo instrumentadas.

Eles explicam em detalhes como as coisas são instrumentadas sob o capô. Talvez você possa encontrar a resposta para suas perguntas lá.

Edwin Dalorzo
fonte
7
Não há novos bytecodes, mas novas estruturas. O verificador vomitará.
Jonathan S. Fisher
12
Um bom exemplo são as interfaces. Agora eles podem conter métodos. O verificador Java7 não está equipado para lidar com isso. Todos os códigos antigos são usados, mas de uma nova maneira.
Jonathan S. Fisher
1
Gostaria de saber como pode o compilador scala com tantos recursos de linguagem alcançar uma versão jvm de destino até do jdk5.
Marinos Um
1
@MarinosAn O que você quer dizer exatamente? MI com traços que contêm métodos concretos, por exemplo class C extends A with B, é implementado com interfaces normais Ae Be classes companheiro A$classe B$class. A classe Csimplesmente encaminha os métodos para as classes complementares estáticas. Auto-tipos não são impostos, lambdas são transpiladas em tempo de compilação para abstrair classes internas, assim como uma new D with A with Bexpressão. A correspondência de padrões é um monte de estruturas if-else. Retornos não locais? mecanismo try-catch do lambda. Sobrou alguma coisa? (Curiosamente, o meu scalac diz 1.6 é o padrão)
Adowrath
1
Obviamente, os tipos próprios etc. são codificados em atributos e anotações especiais de classe, para que o scalac possa usar e impor as regras ao usar classes já compiladas.
Adowrath
-5

Você pode fazer, -source 1.7 -target 1.7então ele será compilado. Mas não será compilado se você tiver recursos específicos do java 8, como lambdas

kalgecin
fonte
A pergunta postula explicitamente o uso dos novos recursos de idioma, portanto -source 1.7não será executada.
toolforger 28/01