Java 8 ao Java 18 em dez minutos ūüöÄ

Java 8 ao Java 18 em dez minutos ūüöÄ

Conheça as grandes melhorias de cada versão do Java

Apr 26, 2022·

10 min read

Vou apresentar as grandes melhorias de cada vers√£o do Java, agora oferece suporte total para lambdas , programa√ß√£o funcional, digite infer√™ncia via var, cole√ß√Ķes imut√°veis com construtores simples e mais perform√°ticos e string multi-linhas.... al√©m disso, existem novos recursos empolgantes, como classes de dados (record), switch 2.0 e sealed classes.

Java 8

Programação Funcional

No Java 8, programa√ß√£o funcional e lambdas foram adicionados como recursos de linguagem. Os dois paradigmas principais de programa√ß√£o funcional est√£o valores imut√°veis e elevando fun√ß√Ķes para cidad√£os de primeira classe.

Os dados passam por um pipeline de etapas de modificação, onde cada etapa pega alguma entrada e mapeia para uma nova saída, que podem ser usadas com Streams e Optional .

Streams

Para um programa de computador comum, geralmente você precisa trabalhar com uma lista de valores e realizar uma determinada transformação em cada valor . Antes do Java 8, você tinha que usar um for loop para essa transformação, mas a partir de agora, você pode usar Streams da seguinte maneira:

Stream.of("Oi", "E ai")
    .map(s -> s + " Sassine")
    .forEach(System.out::println);
> Oi Sassine
> E ai Sassine

A função map recebe como entrada um lambda, que será aplicado a todos os elementos do fluxo.

Streams pode trabalhar em Lists, Sets e Maps (via transformação). Graças ao Streams, você pode se livrar de praticamente todos os loops em seu código! top né ?

Quiser saber mais do Stream, da uma olhada neste post - Java Stream - Domine as listas/cole√ß√Ķes de uma vez por todas; ‚ú®

Optional

Outro problema comum em Java eram as exce√ß√Ķes de ponteiro nulo. Portanto, Java introduziu Optional ‚Äď que envolve uma refer√™ncia que pode ou n√£o ser nula. A aplica√ß√£o de atualiza√ß√Ķes a este opcional pode ser feita de maneira funcional:.

Optional.of(new Random().nextInt(10))
    .filter(i -> i % 2 == 0)
    .map(i -> "n√ļmero √© par: " + i)
    .ifPresent(System.out::println);
> n√ļmero √© par: 6

No trecho acima, criamos um n√ļmero aleat√≥rio, envolvemos dentro de um objeto opcional e, em seguida, imprimimos o n√ļmero apenas se for par.

Quiser saber mais do Optional, da uma olhada neste post - Chega de NullPointer, comece a utilizar Optional em sua codificação!; ✨

Java 9

JShell

Finalmente, temos um REPL para Java, e seu nome √© JShell ! ūüėä Em poucas palavras, JShell permite experimentar trechos de Java sem escrever e compilar uma classe Java completa. Em vez disso, voc√™ pode executar um comando por vez e ver o resultado imediatamente. Aqui est√° um exemplo simples:

$ <JDK_HOME>/bin/jshell > System.out.println("Sassine.dev")
> Sassine.dev

Pessoas familiarizadas com linguagens interpretadas como JavaScript ou Python tiveram o prazer de um REPL por muito tempo, mas at√© agora, esse recurso estava faltando em Java. JShell permite definir vari√°veis , mas tamb√©m entidades mais complexas, como fun√ß√Ķes multilinhas , classes e executar loops.

Al√©m disso, o JShell oferece suporte ao preenchimento autom√°tico, o que √© √ļtil se voc√™ n√£o souber os m√©todos exatos oferecidos por uma determinada classe Java

F√°brica para cole√ß√Ķes imut√°veis

A inicializa√ß√£o simples do Lists est√° faltando no Java h√° muito tempo, mas esses tempos acabaram agora. ūüėÖ Anteriormente, voc√™ tinha que fazer algo assim:

jshell> List<Integer> list = Arrays.asList(1, 2, 3, 4)
list ==> [1, 2, 3, 4]

# Isso agora é simplificado da seguinte forma:

jshell> List<Integer> list = List.of(1, 2, 3, 4)
list  ==> [1, 2, 3, 4]

Este of(...) método sofisticado existe para List , Set e Map . Todos eles criam um objeto imutável em apenas uma linha simples de código.

Java 10

var

Java 10 introduziu a nova palavra-chave var que permite omitir o tipo de uma vari√°vel.

var baos = new ByteArrayOutputStream();

Esse recurso ajuda a reduzir o c√≥digo clich√™ e melhorar a legibilidade. Por√©m, h√° algumas limita√ß√Ķes: voc√™ s√≥ pode usar var dentro dos corpos dos m√©todos, e o compilador inferir√° o tipo em tempo de compila√ß√£o, ent√£o tudo ainda est√° estaticamente tipado.

Java 11

Arquivo de fonte √ļnica

Anteriormente, quando você escrevia um programa Java simples consistindo em um arquivo, era necessário primeiro compilar o arquivo com javac e depois executá-lo java. No Java 11, você pode executar as duas etapas com um comando.

Classe exemplo:

public class TesteCompile {
  public static void main(String[] args) {
    System.out.println("ol√° mundo");
  }
}

Executando a classe:

java ./TesteCompile.java
> ol√° mundo

Para programas iniciais simples ou experimentos que consistem em apenas uma classe Java, esse recurso para iniciar arquivos de c√≥digo-fonte √ļnico tornar√° sua vida mais f√°cil.

Java 12

Switch 2.0

Aqui está uma demonstração rápida de como a expressão difere da antiga instrução switch.

A antiga instrução switch define o fluxo do programa:

jshell> var i = 2
jshell> String s;
jshell> switch(i) {
   ...>     case 1: s = "um"; break;
   ...>     case 2: s = "dois"; break;
   ...>     case 3: s = "três"; break;
   ...>     default: s = "n√ļmero desconhecido";
   ...> }
jshell> s
s ==> "dois"

Agora a nova express√£o switch retorna um valor:

jshell > var i = 2 ;
jshell > var x = switch (i) {
   ... >      case 1 ->  "um";
   ... >      case 2 ->  "dois";
   ... >      case 3 ->  "três";
   ... >      default -> "n√ļmero desconhecido";
   ... > } ;
x ==>  "dois"

Para resumir, a instrução switch antiga é para o fluxo do programa e a nova expressão switch resolve para um valor.

Observe que essa nova instru√ß√£o switch √© uma esp√©cie de fun√ß√£o de mapeamento : h√° uma entrada (no caso acima i) e h√° uma sa√≠da (aqui x). Na verdade, esse √© um recurso de correspond√™ncia de padr√Ķes que ajuda a tornar o Java mais compat√≠vel com os princ√≠pios de programa√ß√£o funcional . Uma instru√ß√£o switch semelhante est√° dispon√≠vel no Scala h√° algum tempo.

Algumas coisas a serem observadas:

  • Em vez de pontos duplos, usamos setas ->
  • N√£o h√° necessidade de break
  • O caso padr√£o pode ser omitido quando todos os casos poss√≠veis s√£o considerados

Para habilitar este recurso com Java 12, use --enable-preview --source 12

Java 13

Strings multilinhas

Voc√™ j√° teve que definir uma string longa de v√°rias linhas como JSON ou XML? At√© agora, voc√™ provavelmente esmagaria tudo em uma linha e usaria caracteres de nova linha \n, mas isso torna a String muito mais dif√≠cil de ler. A√≠ vem o Java 13 com strings de v√°rias linhas ! ūüí™

Se liga só !!

public class TesteCompile { 
  public static void main(String [] args) {
    var s = """
        {
            "nome": "Sassine El-Asmar",
            "idade": "23",
            "stacks": ["backend", "frontend", "mobile"]
        } """;
    System.out.println(s);
  }
}

Agora, executamos o m√©todo principal por meio de inicializa√ß√£o de arquivo √ļnico:

java --enable-preview --source 13 TesteCompile.java

{
  "nome": "Sassine El-Asmar",
  "idade": "23",
  "stacks": ["backend", "frontend", "mobile"]
}

A String resultante se estende por várias linhas, as aspas "" são deixadas intactas e até as guias \t são preservadas!

Java 14

Classes de dados: record (Chega de lombok ?)

Agora existem classes de dados em Java! Essas classes s√£o declaradas com a palavra-chave record e t√™m Getters autom√°ticos, um construtor e o m√©todo equals () etc. Voc√™ pode se livrar de um grande peda√ßo de c√≥digo clich√™! ūüôĆ

jshell> record Pessoa(String nome, int idade) {}
|  created record Pessoa

jshell> var x = new Employee("Sassine", 23);
x ==> Pessoa[nome=Sassine, idade=23]

jshell> x.name()
$2 ==> "Sassine"

Scala tem um recurso semelhante com classes de caso e Kotlin com classes de dados . Em Java, muitos desenvolvedores usaram o Lombok até agora, que oferecia praticamente os recursos que agora inspiravam o records Java 14. Mais detalhes podem ser encontrados neste artigo do Baeldung.

Instanceof j√° devolvendo o objeto tipado

Primeiro verificamos se s é do tipo e String, em seguida, lançamos novamente para recuperar seu comprimento… né?

N√£o mais! agora, com o Java 14, o compilador √© inteligente o suficiente para inferir o tipo automaticamente ap√≥s a verifica√ß√£o de inst√Ęncia :

Object obj = new String("Oi");
if (obj instanceof String s) {
  System.out.println("String length: " + s.length());
}

Java 15

Classes seladas - sealed

Com a palavra-chave sealed , você pode restringir quais classes podem estender uma determinada classe ou interface.

Aqui est√° um exemplo:

public sealed interface Fruit permits Apple, Pear {
    String getName();
}

public final class Apple implements Fruit {
    public String getName() { return "Apple"; }
}

public final class Pear implements Fruit {
    public String getName() { return "Pear"; }
}

Ent√£o, como isso nos ajuda? Bem, agora voc√™ sabe quantos Fruits s√£o. Este √© realmente um passo importante na dire√ß√£o de correspond√™ncia de padr√Ķes totalmente suportada , onde voc√™ pode tratar as classes como enums.

Esse sealed recurso combina perfeitamente com a nova switch express√£o explicada anteriormente.

Java 16

Todos preview listados acima do java 14+ liberado definitivamente

Ou seja n√£o precisa mais do ‚ÄĚ ‚Äďenable-preview ‚ÄĚ para os itens listados acima .

API Vector

A API de vetor fornece um mecanismo para os desenvolvedores tornarem expl√≠cito para o compilador que as opera√ß√Ķes de vetor devem ser usadas. No entanto, isso torna o c√≥digo mais complicado. Primeiro, √© necess√°rio obter uma esp√©cie de vetor.

static final VectorSpecies<Float> SPECIES = FloatVector.SPECIES_256;

void vectorComputation(float[] a, float[] b, float[] c) {
    for (int i = 0; i < a.length; i += SPECIES.length()) {
        var m = SPECIES.indexInRange(i, a.length);

        // FloatVector va, vb, vc;
        var va = FloatVector.fromArray(SPECIES, a, i, m);
        var vb = FloatVector.fromArray(SPECIES, b, i, m);
        var vc = va.mul(va)
           .add(vb.mul(vb))
           .neg();

        vc.intoArray(c, i, m);
    }
}

Java 17

Correspond√™ncia de padr√Ķes para switch (preview)

  • Expanda a expressividade e a aplicabilidade das express√Ķes switch, permitindo que os padr√Ķes apare√ßam nos r√≥tulos de mai√ļsculas e min√ļsculas sem precisar de Instaceof.

  • Introduziu dois novos padr√Ķes: padr√Ķes protegidos e entre par√™nteses.

Implementação antes do JDK17:

public String getValue(Object o) {
        String result = "";
        if(o instanceof Integer i) {
            result = "Integer %d".formatted(i);
        } else if(o instanceof Double d) {
            result = "Double %d".formatted(d);
        } else if(o instanceof Long l) {
            result = "Long %d".formatted(l);
        } else if(o instanceof String s) {
            result = "String %d".formatted(s);
        }
        return result;
}

Vamos refatorar esse método para a nova feature do JDK17:

public String getValue(Object o) {
        return switch(o) { 
        case Integer i -> "Integer %d".formatted(i);
        case Double d -> "Double %d".formatted(d);
        case Long l -> "Long %d".formatted(l);
        case String s -> "String %d".formatted(is);
        default -> o.toString();
        };
}

D√™ uma olhada e um exemplo utilizando os novos padr√Ķes:

public void quantasPortasTem(Veiculo o) {
        switch(o){ 
        case Carro c && (c.getPortas == 2) -> 
        System.out.println("é um carro duas portas");
        case Carro c && (c.getPortas == 4) ->
        System.out.println("é um carro quatro portas");
        default -> System.out.println("não é um carro.");
        };
}

Java 18

UTF-8 por padr√£o

A codifica√ß√£o padr√£o atual pode ser lida em tempo de execu√ß√£o por meio Charset.defaultCharset()da propriedade do sistema "file.encoding". Desde o Java 17 , a propriedade do sistema "native.encoding" pode ser usada para ler a codifica√ß√£o, que ‚Äď antes do Java 18 ‚Äď seria a codifica√ß√£o padr√£o se nenhuma for especificada:

Sendo assim o file.encoding poderia vir como UTF-8 em alguns sistemas, Cp1252 ou default.

Antes:

Default charset : US-ASCII
file.encoding   : default
native.encoding : UTF-8

Agora

Default charset : UTF-8
file.encoding   : UTF-8
native.encoding : Cp1252

Agora no java 18 default não é mais reconhecido e por padrão "file.encoding" é UTF-8

No Linux e MacOS tanto o java 17 e 18 j√° resultam em todas as op√ß√Ķes como UTF-8

Snippets para Java API Doc

Até agora, se quiséssemos integrar trechos de código de várias linhas ao JavaDoc, tínhamos que fazer isso de maneira bastante complicada via <'pre>…</pre'> e algumas regrinhas que tinhamos que seguir.

Agora com a tag @ snippet conseguimos exibir o trecho do código-fonte completo.

/**
 * Escrevendo um arquivo:
 *
 * {@snippet :
 * try (BufferedWriter writer = Files.newBufferedWriter(path)) {
 *   writer.write(text);
 * }
 * }
 */

Integrar trechos de outros arquivos

No FileWriter.java , marcaríamos o código da seguinte forma:

// @start region="writeFile"
try (BufferedWriter writer = Files.newBufferedWriter(path)) {
  writer.write(text);
}
// @end

e utilizamos a referencia dele assim:

/**
 * Escrevendo um arquivo:
 *
 * {@snippet file="FileWriter.java" region="writeFile"}
 */

fim

Bom, listei as principais fun√ß√Ķes novas que tivemos entre as vers√Ķes 8 a 18 ūüėé mas claro que existem muitas outras al√©m de deprecia√ß√Ķes de lib;

Quiser saber mais acesse a documentação oficial da Oracle

Did you find this article valuable?

Support Sassine EL-Asmar by becoming a sponsor. Any amount is appreciated!