A versão de Suporte a Longo Prazo (LTS) da linguagem Java e da plataforma de execução Java 17 foi disponibilizada em 14 de setembro de 2021. Vamos explorar as novidades do Java 17 e analisar se a atualização é recomendável.
Muitos aplicativos ainda utilizam versões mais antigas do Java, inclusive as versões LTS anteriores: Java 11 e Java 8.
Qual a razão para as empresas considerarem a migração para a versão mais recente do Java? A atualização para o Java 17 demanda certo esforço, principalmente para usufruir plenamente dos novos recursos e funcionalidades da JVM.
Uma abordagem comum para facilitar a transição para o Java 17 é o uso de imagens Docker. Desenvolvedores podem configurar seus pipelines de Integração/Implantação Contínua (CI/CD) e operar tudo em imagens Docker. Isso não afeta equipes que usam versões Java mais antigas, que podem continuar com imagens Docker anteriores.
Novidades do Java 17
Aprimoramento do suporte para macOS e AArch64
Um dos avanços cruciais da JVM nesta versão é o suporte aperfeiçoado para macOS na arquitetura AArch64, através do JEP 391. Isso abrange a nova geração de processadores (M1) da Apple, presentes em seus computadores lançados recentemente.
Embora alguns fornecedores já oferecessem versões do JDK compatíveis com essa arquitetura, incluindo suporte retroativo ao Java 8, a validação oficial é fundamental para assegurar a manutenção e o suporte futuro da plataforma. Em contraste, o suporte para Linux/AArch64 foi adicionado no Java 9 e para Windows/AArch64 no Java 16.
Classes Seladas
As Classes Seladas, introduzidas no Java 17, concluem sua fase de testes e se consolidam como um recurso oficial da linguagem. Elas permitem que desenvolvedores definam os subtipos que um tipo pode ter, impedindo extensões ou implementações não desejadas.
Classes seladas também possibilitam que o compilador detecte erros durante a compilação ao tentar converter um tipo não selado em um subtipo não autorizado. O Java 17 também introduz um novo pipeline de renderização para aplicativos AWT/Swing no macOS, utilizando a API Apple Metal ao invés de OpenGL. Adicionalmente, oferece uma API aprimorada e recursos otimizados para gerar números aleatórios.
Alterações, Remoções e Limitações no Java 17
O Java 17 também apresenta diversas mudanças, remoções e novas limitações.
Encapsulamento dos Internos do JDK
Uma das alterações é a conclusão do processo de encapsulamento dos Internos do JDK. Iniciado no Java 9, esse recurso emitia avisos em tempo de execução quando se tentava usar reflexão ou outras técnicas para contornar as restrições de uso das APIs internas. Argumentos de linha de comando também foram adicionados para regular esse comportamento.
A partir do Java 9, foram criadas diversas APIs para padronizar as tarefas mais comuns, com o intuito de que os usuários as utilizassem internamente. No Java 16, o comportamento padrão foi alterado, passando de um aviso para o lançamento de uma exceção em caso de acesso não autorizado. Entretanto, ainda era possível ajustar esse comportamento por meio de argumentos de linha de comando.
No Java 17, o argumento de linha de comando é eliminado, impossibilitando a desativação dessa restrição. Isso implica que todo acesso não autorizado às APIs internas agora está bloqueado.
Semântica de Ponto Flutuante Sempre Estrita
A reintrodução da semântica de ponto flutuante sempre estrita é outra mudança relevante. No Java 1.2, foram introduzidas modificações que permitiam à JVM negociar uma pequena perda de precisão nos cálculos de ponto flutuante para otimizar o desempenho. A palavra-chave strictfp era usada em classes e métodos onde a semântica estrita era necessária. Com a evolução das CPUs e novos conjuntos de instruções, a semântica estrita passou a ser implementada sem custos adicionais. Assim, a necessidade de diferenciar entre semântica padrão e estrita foi eliminada.
O Java 17 remove a semântica padrão anterior e todas as operações de ponto flutuante são executadas estritamente. O termo strictfp permanece, mas não exerce efeito e gera um aviso durante a compilação.
Compilação Antecipada (AOT)
O Java 9 introduziu a compilação antecipada (AOT) como um recurso experimental, utilizando o compilador Graal, com código JIT escrito em Java. O Java 10 integrou o compilador Graal como um compilador JIT no OpenJDK, através da interface JVMCI. Desde sua implementação, essa tecnologia evoluiu significativamente. O compilador Graal teve avanços expressivos e hoje possui sua própria JVM, a GraalVM.
Ativação RMI
A ativação RMI foi eliminada através do JEP 407, após ter sido removida do Java 8, tornada obsoleta e marcada para remoção no Java 15. A ativação RMI fornecia um método para habilitar recursos sob demanda de objetos distribuídos usando RMI. No entanto, seu uso era mínimo e atualmente existem alternativas mais eficientes. A remoção da ativação não afeta o restante do RMI.
Remoção da API de Miniaplicativos
A API de Miniaplicativos foi oficialmente designada para remoção através do JEP 398, sendo inicialmente removida no Java 9. Essa API permitia a integração de controles Java AWT/Swing em páginas web em navegadores. Contudo, nenhum navegador moderno oferece suporte a essa tecnologia, tornando os Miniaplicativos inacessíveis há cerca de uma década.
Gerenciador de Segurança
A depreciação mais relevante é a do gerenciador de segurança (JEP 411). O gerenciador de segurança é usado desde o Java 1.0 para restringir ações do Java localmente na máquina, como limitar o acesso a redes, arquivos e outros recursos. Ele também tentava isolar códigos não confiáveis, bloqueando reflexões e APIs específicas.
O processo de descontinuação do Gerenciador de Segurança começou no Java 12. Foi incluído um argumento de linha de comando para bloquear seu uso em tempo de execução. No Java 17, ao tentar configurar o Gerenciador de Segurança, seja por linha de comando ou dinamicamente em tempo de execução, um aviso será gerado pela JVM.
Recursos em Incubação e Visualização
Muitos questionaram se o Java 17 traria recursos em visualização e incubação, considerando que se trata de uma versão LTS. O Java 17 oferece dois módulos em incubação e um recurso em visualização!
API de Vetores
A API de Vetores (JEP 414) está em sua segunda fase de incubação. Essa API permite que desenvolvedores definam computações vetoriais, que o compilador JIT converte nas instruções vetoriais apropriadas suportadas pela arquitetura de CPU em que a JVM está em execução (como os conjuntos de instruções SSE ou AVX).
Anteriormente, era necessário usar funções escalares ou construir bibliotecas nativas específicas para cada plataforma. A implementação da API Vector em Java também proporciona um mecanismo de fallback contínuo, complexo em versões anteriores.
A padronização da API Vector possibilita seu uso por classes do próprio JDK. Os métodos mismatch() de Java Arrays podem ser modificados para operar em Java, eliminando a necessidade de manter e criar diversas implementações específicas para cada plataforma na JVM.
API de Memória e Função Externa
Outro recurso em incubação é a API de Memória e Função Externa (JEP 412). Ela representa uma evolução e fusão de dois módulos em incubação do Java 16: a API Foreign Linker (JEP 389) e a API de Memória Externa (JEP 393). Ambas possibilitam o acesso a código e memória nativos por meio de programação com tipagem estática escrita em Java.
Correspondência de Padrão para Switch
O último recurso em visualização do Java 17 é a Correspondência de Padrões para Switch (JEP 406). Este recurso de linguagem estende as expressões e instruções do switch com base no tipo, de forma semelhante à sintaxe utilizada na Correspondência de Padrões (JEP 394), que se tornou padrão no Java 16.
Em versões anteriores, para executar diferentes ações com base na natureza dinâmica de um objeto, era necessário construir uma cadeia if-else com verificações de instância:
String type(Object o) {
if (o instanceof List) {
return "Uma lista de itens.";
} else if (o instanceof Map) {
return "Um mapa! Tem chaves e valores.";
} else if (o instanceof String) {
return "Isto é uma string.";
} else {
return "Isso é algo diferente.";
}
}
Ao combinar a expressão switch com o novo recurso de Correspondência de Padrões para switches, o processo pode ser simplificado:
String type(Object o) {
return switch (o) {
case List l -> "Uma lista de itens.";
case Map m -> "Um mapa! Tem chaves e valores.";
case String s -> "Isto é uma string.";
default -> "Isso é algo diferente.";
};
}
Note que há a declaração de uma variável no processo de verificação. Assim como em outros padrões, a correspondência de instância indica que este objeto foi verificado, convertido e está disponível por meio da variável no escopo atual.
O recurso de visualização representa um passo adiante na correspondência de padrões. O próximo passo será a inclusão da capacidade de desconstruir arrays e registros.
A Atualização para o Java 17 é Recomendável?
Sim, é recomendável manter-se atualizado com a versão mais recente, mas não necessariamente no dia do lançamento. O software e as bibliotecas utilizadas podem não ser compatíveis com o Java 17 imediatamente, então pode ser necessário aguardar algum tempo até que isso aconteça.
Se você está utilizando versões LTS mais antigas do Java, como Java 8 ou Java 11, há inúmeras opções na linguagem e na própria JVM que justificam a atualização para o Java 17. Por ser uma versão com suporte de longo prazo, há uma grande chance de que seu ambiente de produção também se beneficie da atualização para o Java 17.
Ao iniciar um novo projeto ou preparar seu projeto para o Java 17, a migração imediata para o Java 17 pode ser a escolha mais eficiente, reduzindo custos de transição. Isso também permite que os desenvolvedores desfrutem de todos os novos recursos, e otimiza os processos operacionais.
É possível aproveitar muitas melhorias que surgiram nos últimos anos, como suporte aprimorado para contêineres executando em Java, e novas implementações de coletores de lixo de baixa latência.