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?
Respostas:
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:fonte
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.7
que seja necessário um retrotranslator.fonte
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 usando
invokeDynamic
(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á.
fonte
class C extends A with B
, é implementado com interfaces normaisA
eB
e classes companheiroA$class
eB$class
. A classeC
simplesmente 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 umanew D with A with B
expressã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)Se você estiver disposto a usar um "retrotranslator", experimente o excelente Retrolambda de Esko Luontola: https://github.com/orfjackal/retrolambda
fonte
Você pode fazer,
-source 1.7 -target 1.7
então ele será compilado. Mas não será compilado se você tiver recursos específicos do java 8, como lambdasfonte
-source 1.7
não será executada.