O que esperar do Java em 2020?

2020 já está em pleno andamento, vamos discutir quais mudanças no mundo Java nos aguardam este ano. Este artigo listará as principais tendências do Java e JDK. E ficarei feliz com as adições dos leitores nos comentários.

Imediatamente, faça uma reserva de que o artigo tem mais caráter de apuração de fatos. Detalhes sobre cada tópico discutido podem ser encontrados no site do projeto correspondente ou em publicações em código aberto.

imagem

Então, vamos começar. Infelizmente, você terá que decepcionar imediatamente aqueles que não seguem muito o ciclo de lançamento do Java, mas aguardam um longo programa de suporte (LTS). Este ano, estamos aguardando lançamentos com apenas um curto ciclo de vida de suporte (STS).
O primeiro, consideraremos o próximo lançamento do JDK 14, que deve ser lançado em meados de março. Neste ciclo de lançamento, até 16 JEPs são reivindicados. Aqui está a lista completa:
305:Correspondência de padrões para instanceof (Preview)
343:Ferramenta de embalagem (incubadora)
345:Alocação de memória compatível com NUMA para G1
349:Fluxo de Eventos JFR
352:Buffers de bytes mapeados não voláteis
358:NullPointerExceptions útil
359:Registros (visualização)
361:Expressões de comutador (padrão)
362:Descontinuar as portas Solaris e SPARC
363:Remova o coletor de lixo simultâneo de varredura de marca (CMS)
364:ZGC no macOS
365:ZGC no Windows
366:Descontinuar a combinação ParallelScavenge + SerialOld GC
367:Remova as ferramentas e a API do Pack200
368:Blocos de texto (segunda visualização)
370:API de acesso a memória externa (incubadora)

Muitos JEPs desta lista foram amplamente abordados na conferência Joker 2019. Vou focar naqueles que me parecem mais interessantes.

Correspondência de padrões para instanceof (Preview)


O longo JEP está finalmente sendo lançado no Preview. Eu acho que se você é um programador praticante que escreve código Java há muitos anos, encontrou essa dor mais de uma vez:

if (obj instanceof String) {
    String s = (String) obj;
    System.out.println(s.toUpperCase());
}

Se você escreveu ou está escrevendo código também no Kotlin, a dor de ver o código Java é um pouco pior. Os participantes do projeto Amber nos apresentarão sua visão de correspondência de padrões em Java, o que deve reduzir essa dor. Com o advento do Java 14, podemos reescrever o exemplo da seguinte maneira:

if (obj instanceof String s) {
   System.out.println(s.toUpperCase());
}

Parece que o complemento não é tão valioso - salvamos uma linha de código. Mas suponha que desejemos fazer o seguinte:

if (obj instanceof String) {
    String s = (String) obj;
    if (s.contains(“prefix_”)) {
       return s.toUpperCase();
    }
}
return null;

Parece volumoso, não é? Vamos tentar a mesma coisa, mas com a correspondência de padrões.

return (obj instanceof String s) && s.contains(“prefix_”) ? s.toUpperCase() : null;

Então, obviamente, será melhor. Mas lembre-se de que o status dessa funcionalidade é Visualização. Vamos ver o que muda com o tempo. Para mim, isso definitivamente tornará minha vida melhor.

NullPointerExceptions útil


2020 está no quintal, e você ainda escreve para que NullPointerExceptions voe para você? Não se preocupe, você provavelmente não é o único. Goetz Lindenmaier e Ralf Schmelter não sugeriram uma nova maneira de se afastar do NullPointerExceptions (o Opcional ainda está conosco), mas propuseram melhorar o processo de depuração do aplicativo para entender exatamente onde está o nulo. Então, vamos imaginar que escrevemos o código às cinco horas ... à noite, é claro. E nós escrevemos esta função:

public String getStreetFromRequest(Request request) {
   return request.getAddress().getStreet().toUpperCase();
}

Nada mal, mas eles esqueceram completamente de colocar as anotações @Nullable e @Nonnull e verificar o endereço nos campos transmitidos. Obteve uma NullPointerException. O que a exceção nos diz?

Exception in thread "main" java.lang.NullPointerException
	at Program.getStreetFromRequest(Program.java:10)
	at Program.main(Program.java:6)

Infelizmente, vemos apenas uma linha, uma classe e uma pilha. Para onde exatamente o nulo retornou? Talvez isso seja um pedido? Talvez getAddress () retornou nulo? Ou talvez getStreet ()? Bem, às vezes as cadeias de chamadas doem.

Os autores do JEP propõem uma solução: ao lançar uma exceção, deve-se contornar a pilha para determinar onde exatamente nulo retornou e, em seguida, exibir o nome das variáveis ​​/ métodos. Vamos tentar o Java 14 com a opção -XX: + ShowCodeDetailsInExceptionMessages. Começamos e ficamos um pouco diferentes:

Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.toUpperCase()" because the return value of "Address.getStreet()" is null
	at Program.getStreetFromRequest(Program.java:10)
	at Program.main(Program.java:6)

Agora sabemos que a programação noturna não é boa (mas às vezes leva à conclusão das tarefas no prazo) e, em nosso programa, esquecemos que o endereço não é um campo obrigatório.

Registros (visualização)


Ainda gerando getters / setters / iguais / hashCode com a idéia? Então este JEP está chegando até você!
As classes de dados estão longe de ser o último lugar na vida de um desenvolvedor de software de aplicativo. Cada vez que precisamos gerar métodos de classes Data usando nosso IDE favorito ou usar vários plugins em tempo de compilação para gerar os métodos necessários, como o lombok.

Como resultado, temos muito código semelhante a este:

public class Request {
    private Address address;

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
       this.address = address;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Request request = (Request) o;
        return Objects.equals(address, request.address);
    }

    @Override
    public int hashCode() {
        return Objects.hash(address);
    }

    @Override
    public String toString() {
        return "Request{" +
                "address=" + address +
                '}';
    }
}

Ou tal:
@Data
@AllArgsConstructor
public class Request {
    private Address address;
}

No Java 14, os membros do projeto Amber propõem uma nova sintaxe para criar classes de dados. Para fazer isso, use o novo registro de palavra-chave. A sintaxe para Record é um pouco diferente da descrição de classe ou enum e é ligeiramente semelhante ao Kotlin. O código acima pode ser reescrito da seguinte maneira:

public record Request(Address address) {
}

Todos os campos de registro possuem modificadores privados e finais por padrão. O próprio registro é uma classe final e não pode ser herdado de outra classe, mas pode implementar interfaces. Nas classes de registro da caixa, obtemos: métodos getters, um construtor público, cujos parâmetros são todos os campos de registro na ordem de descrição, igual a / hashCode e toString. Do desagradável: não podemos adicionar campos ao registro, exceto aqueles especificados após o nome da classe. Por exemplo, este código resultará em um erro:

public record Request(Address address) {
   private final String anotherParameter; // compilation error
}

Blocos de texto (segunda visualização)


Os blocos de texto foram liberados como visualizações no Java 13. Nós, usuários, giramos, giramos e fornecemos feedback (acredito sinceramente que você já está usando o Java 13 com visualização). Como resultado, os criadores de Java fizeram pequenas alterações nos blocos de texto. Primeiro, agora podemos indicar explicitamente onde a linha termina, colocando a sequência de escape \ s em nossa linha. Aqui está um exemplo:

public static void main(String[] args) {
        final var test = """
                This is the long text block with escape string \s
                that is really well done            \s
                """;
        System.out.println(test);
}

Agora, definimos explicitamente todos os espaços para o caractere \ s e todos os caracteres de espaço serão salvos no caractere \ s.

Em segundo lugar, agora podemos quebrar linhas longas de um bloco de texto sem receber \ n caracteres na linha final. Para fazer isso, precisamos apenas adicionar \ na quebra de linha. Com o que se parece:

public static void main(String[] args) {
        final var test = """
                This is the long text block with escape string \
                that is really well-done functional            
                """;
System.out.println(test);

Após a execução, obtemos a seguinte linha: “Este é o bloco de texto longo com a string de escape que é realmente muito bem-funcional”.

Uma boa adição, parece-me. Estou realmente ansioso para traduzir essa funcionalidade em padrão.
Todos os recursos que analisamos provavelmente serão amplamente discutidos nas próximas conferências. Alguns deles já foram discutidos no Joker 2019. Confira a palestra do Joker 2019 sobre "Evolução dos recursos em Java 13 e além", de Cay Horstmann.

E algumas coisas mais interessantes.


Existem dois itens interessantes na lista do JEP na incubadora. Para começar, teremos uma ferramenta universal que criará instaladores para diferentes sistemas operacionais (bem, finalmente, quero dizer para aqueles que dançaram ao redor da instalação de programas no Windows). O Jpacker poderá criar instaladores msi / exe para Windows, pacotes macOS e rpm / deb para Linux. Vamos ver o que acontece, mas nesses raros casos em que fiz algo para desktop, sofri pessoalmente o fato de não ter uma ferramenta regular para montar o instalador.

Ainda mais promissora é a nova API para acessar "Memória externa", ou seja, qualquer tipo de memória nativa ou persistente. Essa API é útil principalmente para criadores de banco de dados Java ou criadores de estrutura, como o Netty, por exemplo. Eles, usando o Unsafe e o ByteBuffer, otimizam o acesso à memória com a falta de heap o máximo possível.

Próximo lançamento. Alegria e frustração


Em setembro, aguardamos outro lançamento de suporte de curto prazo no número 15. A lista de JEPs que serão incluídos no release final ainda está aberta. Até o momento, é possível ver muitas mudanças diferentes no próprio idioma, na biblioteca padrão e na máquina virtual.
Aqui está a lista de candidatos (ela pode mudar rapidamente, veja aqui: bugs.openjdk.java.net/secure/Dashboard.jspa?selectPageId=19114 ):
111:Additional Unicode Constructs for Regular Expressions
116:Extended Validation SSL Certificates
134:Intuitive Semantics for Nested Reference Objects
152:Crypto Operations with Network HSMs
198:Light-Weight JSON API
218:Generics over Primitive Types
300:Augment Use-Site Variance with Declaration-Site Defaults
301:Enhanced Enums
302:Lambda Leftovers
303:Intrinsics for the LDC and INVOKEDYNAMIC Instructions
306:Restore Always-Strict Floating-Point Semantics
338:Vector API (Incubator)
339:Compiler Intrinsics for Java SE APIs
348:Compiler Intrinsics for Java SE APIs
356:Enhanced Pseudo-Random Number Generators
360:Sealed Types (Preview)
371:Hidden Classes

Como você pode ver, a lista ainda não tem muita coisa esperada. Primeiro de tudo, para mim é o Projeto Loom. A idéia de paralelismo estrutural tem sido muito popular nos últimos anos. As corotinas podem simplificar bastante a tarefa de computação paralela competitiva e execução assíncrona de tarefas. Grandes exemplos de implementação dessa idéia podem ser vistos, por exemplo, nas línguas Kotlin (coroutines) e Go (goroutines). Java também está explorando a idéia de paralelismo estrutural, e já existem primeiros resultados. Por enquanto, você só pode vê-los compilando o JDK a partir do repositório do projeto.

Um projeto muito promissor Valhalla também ainda não nos agradou com nenhuma prévia. Um relatório interessante sobre esse projeto foi apresentado no Joker 2019 ("O Java requer tipos" em linha "? Uma visão restrita do engenheiro de desempenho no projeto Valhalla", de Sergey Kuksenko).

O que é apresentado na lista?

A primeira coisa que chama sua atenção é a API JSON. A questão surge imediatamente - por quê? Claramente, não há uma resposta definitiva. A seção JEP sobre motivação diz que o JSON se tornou um padrão para os serviços da Web, e agora é hora de adaptar o Java SE para interagir com o JSON (mesmo que haja uma tonelada de bibliotecas para analisar o JSON agora). A explicação mais provável é a capacidade de os desenvolvedores de software usarem uma API de núcleo pequeno para reduzir o tamanho do pacote sem precisar arrastar o pesado Jackson para si. Não vejo nenhuma outra explicação, porque ela nem terá ligação de dados.

Também vemos várias melhorias relacionadas à API criptográfica. Para começar, os desenvolvedores do JDK desejam expandir o processo de validação de certificados SSL adicionando suporte a certificados EVSSL. Usando esta API em Java, você pode determinar se uma conexão SSL é confiável por um certificado EV (Validação Estendida). O certificado EVSSL de acordo com a diretriz será totalmente suportado. Um novo algoritmo criptográfico EdDSA também será adicionado e a verificação da criptografia HSM será aprimorada.

Nas coisas da linguagem, eu destacaria a implementação de Genéricos em primitivas. Todo mundo que já programou em C # e mudou para Java, provavelmente poderia fazer a pergunta: por que você não pode fazer tipos genéricos em primitivos. A resposta é simples - os genéricos funcionam apenas com objetos, e as primitivas não são objetos e, em particular, não herdam uma classe Object. Não é o primeiro ano em que uma guerra foi travada sobre esse assunto, e Brian Goetz está voltando a ele novamente. Não há nada de especial para descrever até agora. A tarefa é clara: oferecer suporte a construções como Lista. Mas, mesmo no momento, existem 13 questões em aberto que precisam ser resolvidas antes de implementar essa funcionalidade. Honestamente, eu me pergunto como essa série terminará.

E a última coisa que quero abordar são os tipos selados. Este é o próximo passo para a correspondência de padrões. Tipos selados é uma extensão de idioma que implementa o selado (modificador) e permite palavras-chave para uma classe ou interface.

Usando a classe selada, limitamos o número de descendentes apenas às classes especificadas em licenças (uma restrição explícita) ou na mesma unidade de compilação (arquivo). Exemplo de descrição de uma classe selada:

// 
public abstract sealed class Base {
   public static class ChildA extends Base{}
   public static class ChildB extends Base{}
}

// 
public sealed interface BaseInterface permits ChildC, ChildD{
}

//  
public class ChildC implements BaseInterface {
}
//  
public class ChildD implements BaseInterface {
}

O modificador selado garante que apenas um conjunto finito limitado de descendentes possa estender a classe base ou implementar uma interface. Esta propriedade pode ser usada ao processar objetos dessas classes. E, é claro, este é um ótimo candidato para uso em uma instrução switch com correspondência de padrões.

Conclusão


Analisamos as várias inovações do JDK este ano. Alguns deles vão atirar, outros não. Mas, acima de tudo, nos novos JDKs, espero novas otimizações pequenas (ou não) que tornam nossos programas mais rápidos a cada versão gratuitamente. E se você esteve no último Joker 2019 e visitou o relatório Tagir Valeev Java 9-14: Pequenas otimizações, provavelmente, como eu, ficou impressionado com o trabalho que os colaboradores fazem para otimizar o JDK. Seus resultados não são visíveis à primeira vista e não são refletidos em mais de um PEC, mas os usamos todos os dias.

Boas versões de Java para todos nós. Explore os novos recursos da plataforma, acesse conferências e acompanhe as tendências.

All Articles