Java 14: Record, uma instância mais concisa de, wrapper jpackage, alternar lambdas e blocos de texto

UPD. Hoje será o tão esperado lançamento do Java 14 - e mesmo que não seja o LTS - existem novos recursos suficientes. O Java 14 estará disponível em algumas horas - mas você pode conhecê-lo agora.



No Java 14, há mudanças suficientes, tanto no nível de escrita do código quanto no nível da API, GC e muitos outros mecanismos. Podemos dizer com certeza que, se você conhece alguns super-chips Kotlin ou Python - não se preocupe, com um alto grau de probabilidade, eles aparecerão em breve em Java. De qualquer forma, o release de hoje contém alguns deles. Mas as primeiras coisas primeiro.

No Java 14, as seguintes inovações nos aguardam:

  • JEP 305. Atribuindo uma referência a um objeto que está sendo verificado através da instanceof.
  • JEP 343. O empacotador jpackage (Incubadora).
  • JEP 345. Alocação de memória baseada em NUMA para G1.
  • JEP 349. Streaming de eventos através do JFR.
  • JEP 352. Buffers de bytes mapeados imutáveis.
  • JEP 358. Dicas sobre NullPointerException.
  • Jep 359. Record.
  • JEP 361. Alterne lambdas.
  • JEP 362. As portas Solaris e SPARC agora estão obsoletas.
  • JEP 363. Removendo o coletor de lixo Concurent Mark Sweep que foi marcado anteriormente como Descontinuado.
  • JEP 364. Portando o ZGC no macOS.
  • JEP 365. Portando o ZGC para o Windows.
  • JEP 366. A combinação de ParallelScavenge + SerialOld GC agora está obsoleta.
  • JEP 367. Removendo ferramentas e APIs do Pack200 (que foram marcadas como Descontinuadas no Java 11).
  • JEP 368. Blocos de texto.
  • JEP 370. API de acesso à memória externa (incubadora).

Vamos falar sobre cada melhoria, algumas das quais serão discutidas em mais detalhes.

JEP 305. Atribuindo uma referência a um objeto verificado através da instância de




Considere a situação de verificar o tipo de um objeto por meio de instanceof. Se queremos personalizar explicitamente um objeto para o tipo necessário sem arriscar uma ClassCastException, primeiro precisamos ter certeza de que o objeto é do tipo que precisamos.

    private static void showNameIfToy(Object o) {
        if (o instanceof Toy) {  //,    -  Toy
            Toy t = (Toy) o;  //  
            System.out.println("Toy's name: " + t.getName());  //-   
        }
    }

Neste exemplo, que ocorre, a propósito, o tempo todo, a) certificamos que temos um objeto do tipo desejado, b) atribuimos um link a ele, c) fazemos algo com ele, com base em nossa lógica. Na Oracle, ao que parece, eles viram um certo esplendor de código nessa construção específica e decidiram reduzi-lo em exatamente uma etapa. Agora você pode escrever assim:

    private static void showNameIfToy(Object o) {
        if (o instanceof Toy t) {  //      
            System.out.println("Toy's name: " + t.getName());  //     
        }
    }

Como tudo isso é realmente conveniente e útil, deixo para julgá-lo, leitor. Sim, de fato, essa construção dessa forma ocorre, provavelmente, em 99% dos casos associados à instância de e, talvez, a simplificação se enraíze (ou talvez não). O tempo vai dizer.

JEP 343. O jpackage Packer (Incubadora)




Nós, os desenvolvedores, somos caras, você não vai nos assustar com a instalação de um dzharnik, mas e quanto a um usuário simples que não quer saber ao instalar a JVM que ele está instalado em outros 3 bilhões de máquinas, não quer saber sobre a plataforma cruzada Java, mas só quer cutucar 2 vezes o arquivo .exe se tiver Windows ou simplesmente arraste o aplicativo .app para a pasta Aplicativos, se houver uma papoula, e não o vaporize? Como criar todas as condições para os programadores para que eles empacotem seus aplicativos em "executáveis" familiares ao usuário final?

Parece que a Oracle percebeu o problema e decidiu escrever um empacotador que empacotará imediatamente os aplicativos em um formato adequado para a plataforma:

  • Linux: deb e rpm
  • macOS: pkg e dmg
  • Windows: msi e exe

Parece algo como isto:

$ jpackage --name myapp --input lib --main-jar main.jar --type exe

--name myapp - nome futuro do aplicativo
--input lib - fonte do arquivo jar
--main-jar main.jar - nome do arquivo que contém a classe principal
--type exe - tipo no qual o aplicativo será empacotado.

Após o empacotamento, você pode clicar duas vezes no myapp.exe (se você tiver o Windows) e instalá-lo como um aplicativo normal do Windows. A interface gráfica é fornecida pelo JavaFX.

Vamos tentar criar esse aplicativo usando a plataforma Windows.

Então, vamos pegar o projeto a partir daqui:
https://github.com/promoscow/bouncer
Ao enviar uma solicitação GET, recebemos a mensagem: “Rejeição bem-sucedida” e carimbo de data e hora.

Colocando um dzharnik. Como temos gradle, ele está na pasta build / libs.



Vá para a pasta build, insira o mínimo necessário de comandos:

jpackage --name bouncer --input libs --main-jar bouncer.jar --type exe

Na verdade, temos um exe-shnik.



Nós cutucamos duas vezes no executável, a instalação usual do aplicativo ocorre.
Uau!



O aplicativo foi instalado e está esperando nos bastidores.
Em Arquivos de Programa, na pasta bouncer, você pode executar o bouncer.exe.



JEP 345. Alocação de memória alocada ao NUMA para G1


Existe um problema - NUMA, acesso não uniforme à memória, acesso desigual à memória. Em outras palavras, o acesso a soquetes remotos em máquinas de várias seções pode levar um tempo considerável, especialmente para o coletor de lixo G1. No Java 14, eles tentaram resolver esse problema da seguinte maneira: O

heap G1 é organizado como um conjunto de áreas de tamanho fixo. Uma região geralmente é uma coleção de páginas físicas, embora ao usar páginas grandes (via -XX: + UseLargePages), várias regiões possam formar uma página física.

Se você adicionar o parâmetro + XX: + UseNUMA durante a inicialização da JVM, as regiões serão distribuídas uniformemente pelo número total de nós NUMA disponíveis.

A Oracle promete que, embora essa abordagem não seja totalmente flexível, ela será aprimorada no futuro.

JEP 349. Streaming de eventos através do JFR


Em Java, existe algo como JFR - Java Flight Recording - gravação de eventos em tempo real, que permite monitorar cerca de 500 variedades de eventos enquanto o aplicativo está em execução. O problema é que a maioria deles só pode ser visualizada nos logs. A melhoria oferecida pela Oracle é implementar um manipulador, por exemplo, uma função lambda que será chamada em resposta a um evento.

Aqui está o que parece:

try (var rs = new RecordingStream()) {
  rs.enable("jdk.CPULoad").withPeriod(Duration.ofSeconds(1));
  rs.onEvent("jdk.CPULoad", event -> System.out.println(event.getFloat("machineTotal")));
  rs.start();
}

Este evento a cada segundo exibe a carga do processador no console.



JEP 358. Dicas sobre NullPointerException


Outra conveniência óbvia, projetada para simplificar a busca por erros no código. Imagine uma construção - um planeta, existem muitos países no planeta, em todos os países existem muitas cidades.

public class Planet {

    private List<Country> countries;
    //...
}

public class Country {

    private List<City> cities;
    //...
}

public class City {

    private String name;
    //...
}

Decidimos exibir os códigos de hash de todas as cidades do planeta:

planet.getCountries().forEach(c -> c.getCities().forEach(city -> city.hashCode()));

Mas eles não pensaram na inicialização obrigatória dos campos. E em algum momento eles receberam uma NullPointerException:

Exception in thread "main" java.lang.NullPointerException
	at ru.xpendence.jep_358_nullpointerexception.Main.main(Main.java:19)

Qual campo temos nulo? planeta? país? cidade ??? Nós não sabemos. Colocamos o ponto de interrupção na linha certa e, com um suspiro, nos degradamos.

No Java 14, uma NullPointerException é mais informativa:

Exception in thread "main" java.lang.NullPointerException: Cannot assign field "cities" because "countries" is null
     at Main.main(Main.java:21)
     ...

E imediatamente tudo está claro. países é nulo.

JEP 359. Record




Não é de admirar que a Oracle tenha alterado o ciclo de lançamento para seis meses. As mudanças tectônicas no setor de TI fazem com que até esses líderes se movam mais rapidamente. E se, por exemplo, Kotlin e Python no momento de sua aparência foram influenciados pelo Java (é o que a Wikipedia diz sobre o Python, de qualquer maneira), agora o Java está olhando para seus seguidores, por assim dizer. Sobre o Python, ele será menor, mas o seguinte recurso do Oracle foi analisado com precisão no Kotlin. É sobre classes de dados, que em Java agora são chamadas de registro.

O que é uma classe de dados? Vamos escrever um POJO comum:

public class Station {

    private String name;
    private Coordinates coordinates;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Coordinates getCoordinates() {
        return coordinates;
    }

    public void setCoordinates(Coordinates coordinates) {
        this.coordinates = coordinates;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        PlainStation that = (PlainStation) o;
        return Objects.equals(name, that.name) &&
                Objects.equals(coordinates, that.coordinates);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, coordinates);
    }

    @Override
    public String toString() {
        return "PlainStation{" +
                "name='" + name + '\'' +
                ", coordinates=" + coordinates +
                '}';
    }
}

Aqui temos tudo: getters, setters, iguais, hashcode, toString ... Para, de alguma forma, livrar-se dessa desgraça, pessoas boas criaram Lombok.

No Jetbrains, o problema foi resolvido de uma maneira mais radical - inventando classes de dados para o Kotlin. Essa classe com todos os métodos padrão se parece com isso:

data class Station(val name: String, val coordinates: Coordinates)

E é isso. No Oracle, ao que parece, analisou esse design e fez exatamente o mesmo, apenas registrando:

public record RecordStation(String name, List<Coordinates> coordinates) {}

Ele contém getters, setters, iguais, hashcode e toString padrão.
Uma classe desse tipo já pode ser criada no IDEA 2020.1.

Quais são as diferenças do POJO?


  • O registro não pode ser herdado de nenhuma classe.
  • O registro não pode ter outros campos do objeto, exceto aqueles declarados no construtor ao descrever a classe (sim, a propósito, esse é o construtor padrão). Estático - você pode.
  • Os campos são implicitamente finais. Objetos são implicitamente finais. Com todas as consequências, como a impossibilidade de ser abstrato.

Acontece que essa é uma classe de dados imutável, não destinada a algumas ações lógicas complexas, mas destinada à transferência de dados, e isso é tudo.

JEP 361. Switch Lambdas




Parece que a Oracle aceitou o interruptor firmemente. Antes do lançamento atual, era um design bastante volumoso e tinha a seguinte aparência:

    public static void translateDayOfWeekOld(String dayOfWeek) {
        switch (dayOfWeek) {
            case "MONDAY":
                System.out.println("");
                break;
            case "TUESDAY":
                System.out.println("");
                break;
            case "WEDNESDAY":
                System.out.println("");
                break;
            case "THURSDAY":
                System.out.println("");
                break;
            case "FRIDAY":
                System.out.println("");
                break;
            case "SATURDAY":
                System.out.println("");
                break;
            case "SUNDAY":
                System.out.println("");
                break;
            default:
                System.out.println("Day of week not found, try again with today day of week");
                String displayName = LocalDate.now().getDayOfWeek().name();
                translateDayOfWeek(displayName);
        }
    }

O processamento de uma condição requer pelo menos três linhas - caso, ação, interrupção. Agora, com a funcionalidade aprimorada do switch, podemos reduzir a construção acima para isso:

    public static void translateDayOfWeek(String dayOfWeek) {
        switch (dayOfWeek) {
            case "MONDAY" -> System.out.println("");
            case "TUESDAY" -> System.out.println("");
            case "WEDNESDAY" -> System.out.println("");
            case "THURSDAY" -> System.out.println("");
            case "FRIDAY" -> System.out.println("");
            case "SATURDAY" -> System.out.println("");
            case "SUNDAY" -> System.out.println("");
            default -> {
                System.out.println("Day of week not found, try again with today day of week");
                String displayName = LocalDate.now().getDayOfWeek().name();
                translateDayOfWeek(displayName);
            }
        }
    }

Concordo, bastante compacto, elegante e com lambdas. Vale a pena, você decide.

A propósito, a mudança de IDEA 2020.1, escrita de acordo com as regras antigas, sugere cuidadosamente reescrever de uma nova maneira.



JEP 362. Portas Solaris e SPARC agora obsoletas


Tudo é simples aqui. A Oracle decidiu que não valia a pena gastar recursos no suporte a portas Solaris e SPARC, e os funcionários liberados deveriam mudar para o desenvolvimento de novos recursos.

JEP 363. Removendo o coletor de lixo Concurent Mark Sweep que foi marcado anteriormente como Descontinuado


A remoção do coletor de lixo do CMS foi discutida há dois anos, no nono lançamento. Durante esse período, dois coletores de lixo foram lançados - ZGC e Shenandoah. Ao mesmo tempo, nenhum dos colaboradores confiáveis ​​da Oracle prestou atenção no suporte ao CMS.

Em geral, o médico disse ao necrotério - depois ao necrotério.

JEP 364, 365. Portando o ZGC no macOS e Windows


Normalmente, implantamos aplicativos nas plataformas Linux, mas, para o desenvolvimento local, geralmente usamos o Windows e o Mac. Para essas necessidades, o Java 14 portou o coletor de lixo ZGC para essas duas plataformas.

JEP 366. Combinação ParallelScavenge + SerialOld GC agora descontinuada


Há uma configuração de coletor de lixo usada muito raramente - quando o jovem ParallelScavenge é combinado com o SerialOld. Por que isso é feito não está claro, pois ParallelScavenge é paralelo e SerialOld, pelo contrário, não é. A inclusão dessa combinação requer danças especiais com um pandeiro e requer muitos desenvolvedores de sangue. “A Oracle se preocupa com você”, por isso marca essa configuração como obsoleta e espera enviá-la em breve ao coletor de lixo do CMS.

Vamos nos alegrar, irmãos.

JEP 367. Removendo as ferramentas e APIs do Pack200 (que foram marcadas como Descontinuadas no Java 11)


A virada do pack200, unpack200 e API pack200 veio para um descanso bem merecido. E o motivo é a obsolescência desse empacotador. Era uma vez, quando a Internet era modem e sua velocidade era de 56k (lembram-se os boomers), o JDK teve que ser bombeado por horas. Foi inventado um empacotador que comprimiria o JDK melhor e reduziria o tempo de download. Além disso, eles poderiam compactar applets e aplicativos clientes. Mas o tempo passou e, nas velocidades atuais, o empacotador não é relevante.

Os seguintes pacotes serão removidos:

java.util.jar.Pack200
java.util.jar.Pack200.Packer
java.util.jar.Pack200.Unpacker

, bem como o módulo jdk.pack.

JEP 368. Blocos de Texto




Ao mesmo tempo, o Java teve (entre uma meia dúzia de outras linguagens) um impacto no Python. Como o Python está rapidamente ganhando popularidade e avançando com confiança com outros líderes dos gráficos Java e C IT, não há nada de errado em espiar. Ao mesmo tempo, o Java 9 introduziu o JShell, muito semelhante ao pythonium Jupiter. E agora, chegou a hora dos blocos de texto.

Quando precisamos escrever uma linha, escrevemos uma linha:

String s = "";

Quando precisamos escrever uma string formatada, escrevemos algo como isto:
String oldHtml = "<html>\n\t<body>\n\t\t<p>Hi all!</p>\n\t</body>\n</html>";

O texto é completamente ilegível. E assim, no Java 14, o problema está resolvido. Agora, usando aspas triplas, você pode escrever qualquer texto:

        String html = """
                      <html>
                          <body>
                              <p>Hi all!</p>
                          </body>
                      </html>
                      """;

É muito mais conveniente e legível. Podemos simplesmente copiar qualquer texto no código e não nos incomodar com guias e hífens. A beleza! Se precisarmos apenas transferir o texto para outra linha desse texto sem fazer hífen, podemos usar um novo literal - a barra invertida. Este símbolo generaliza que a transferência a seguir não é uma transferência. Exemplo:

        String text = """
                    \
                 ,  , \
                     \
                   . \
                , ,  ! \
                  ; \
                    \
                   .
                """;

Conclusão:

Os deuses ainda lhe deram dias dourados, noites douradas, e virgens lânguidas fixadas em seus olhos atentos. Toca, canta, ó amigos! Perca uma noite fugaz; E sua alegria pelo descuido Através das lágrimas eu sorrio.


JEP 370. API de acesso à memória externa (incubadora)


Acontece que um aplicativo acessa memória externa como Ignite, mapDB, memcached, etc. As APIs existentes para acessá-lo estão funcionando, mas a Oracle queria algo global. Então, as abstrações de MemorySegment, MemoryAddress e MemoryLayout apareceram. Enquanto o recurso estiver na incubadora, todos poderão se contentar com ByteBuffer, Unsafe e JNI.

Conclusão


Não sei você, leitor, mas gosto do ciclo de seis meses para o qual a Oracle mudou desde o Java 9. Agora, a Oracle não define a tarefa de liberar versões absolutamente estáveis, mas a sofisticada javista não será difícil de manter a par da estabilidade de um recurso ou de outro, observando pelo desenvolvimento e teste de algo da incubadora. O idioma se tornou mais vibrante, mutável, inovações muito ousadas e empréstimos de outros idiomas começaram a aparecer nele. Sim, alguém não acompanha a oscilação dos lançamentos, mas nossa profissão é tal que precisamos acompanhar, portanto, quer desejemos ou não, encontramos o Java 14.

Como sempre, estou anexando um projeto em um github com exemplos de código: [clique aqui]

All Articles