Top 5 novidades do Java 14

Cayo Souza
mobicareofficial
Published in
7 min readMar 17, 2020

--

Introdução

Hoje, 17 de março de 2020, está sendo lançada a versão 14 do Java trazendo mais JEPs (Java Enhancement Proposals) que as duas últimas versões (12 e 13) juntas!

Das 16 JEPs contidas nesta versão, estarei abordando as 5 (em ordem crescente do número das JEPs) que estão sendo mais comentadas.

Antes de continuar, gostaria de explicar a nomenclatura “preview”, que são features em que a arquitetura, especificação e implementação estão completas mas ainda não estão em seu estado final. E na prática isto significa que esta feature pode ser alterada ou descontinuada em futuras versões da linguagem.

Então sem mais delongas, vamos às novidades!

Pattern Matching for instanceof (Preview) [JEP 305]

Pattern Matchings” são sintaxes convenientes para extrair componentes de objetos baseado em condições específicas.

Esta JEP tem como objetivo diminuir a verbosidade do uso do “instanceof”, que testa o tipo de uma classe, através da remoção da necessidade de “casts” explícitos.

O código a seguir é muito comum quando usamos o instanceof:

if (obj instanceof String) {
String s = (String) obj;
// usa s
}

Nele observamos que 3 coisas estão sendo feitas: um teste (if), uma conversão (casting pra String) e declaração de variável (s).

Com o pattern matching este mesmo resultado pode agora ser obtido da seguinte forma:

if (obj instanceof String s) {
// agora poderá usar s aqui
}

Em apenas uma linha conseguimos fazer estas mesmas 3 coisas de uma só vez!

Esta mudança permitirá a remoção de muitos casts redundantes já que, segundo este artigo científico, 24% de todos os casts procedem instanceofs.

Esta JEP também aponta um outro exemplo, desta vez no famoso método “equals”, que usamos para comparar objetos, que antes era escrito assim:

@Override 
public boolean equals(Object o) {
return (o instanceof CaseInsensitiveString) &&
((CaseInsensitiveString) o).s.equalsIgnoreCase(s);
}

E após aplicar o pattern ficou assim:

@Override 
public boolean equals(Object o) {
return (o instanceof CaseInsensitiveString cis) &&
cis.s.equalsIgnoreCase(s);
}

Sem dúvidas este é um ótimo preview e pode ser só o começo de muitos outros patterns!

Helpful NullPointerExceptions [JEP 358]

Este camarada dispensa comentários, o erro mais conhecido e uns dos mais comuns do Java. O NPE (Null Pointer Exception) é lançado quando tentamos acessar alguma informação de um objeto nulo.

Atualmente a JVM (Java Virtual Machine) nos informa a linha da origem do problema, por exemplo:

a.b.c = 99;

Irá lançar o NPE:

Exception in thread "main" java.lang.NullPointerException at Prog.main(Prog.java:5)

A única informação que temos é que ocorreu na linha 5. Mas foi causado pelo “a” ou “b”? A partir de hoje saberemos:

Exception in thread "main" java.lang.NullPointerException: Cannot read field "c" because "a.b" is null at Prog.main(Prog.java:5)

Ficou claro que o NPE está sendo causado pelo “b”!

E ganhamos duas novas informações: a causa: “a.b is null” — e a consequência: “Cannot read field c”.

O mesmo se aplica aos outros meios que lançam NPE, como vetores:

a[i][j][k] = 99;Exception in thread "main" java.lang.NullPointerException: Cannot load from object array because "a[i][j]" is null at Prog.main(Prog.java:5)

Mas atenção! Esta mensagem não virá habilitada por padrão, necessitando passar como parâmetro ao executar o código a seguinte flag:

-XX:+ShowCodeDetailsInExceptionMessages

Por exemplo:

java -XX:+ShowCodeDetailsInExceptionMessages SuaClasse

Uma excelente novidade para o “debug” nosso de cada dia!

Records (Preview) [JEP 359]

Um novo “preview” que também ataca a verbosidade de maneira brutal. Records prometem reduzir drasticamente as linhas de código associadas às classes que só armazenam dados, também conhecidas como POJO (Plain Old Java Object) ou classes do Model (em arquiteturas MVC).

Antes de mais explicações vamos ao exemplo, uma classe Ponto com atributos x e y:

record Ponto(int x, int y) { }

Esta linha de código simplesmente equivale a:

public class Ponto {
private final int x;
private final int y;
public Ponto(int x, int y) {
this.x = x;
this.y = y;
}
public int x() {
return x;
}
public int y() {
return y;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Ponto ponto = (Ponto) o;
return x == ponto.x &&
y == ponto.y;
}
@Override
public int hashCode() {
return Objects.hash(x, y);
}
@Override
public String toString() {
return "Ponto{" +
"x=" + x +
", y=" + y +
'}';
}
}

O record equivale a uma classe com:

  • Um atributo privado e final para cada argumento informado;
  • Um construtor público que utiliza todos os argumentos;
  • Getters;
  • toString;
  • equals e hashCode

Para habilitar este preview, use a seguinte flag:

javac --enable-preview --release 14 BankTransaction.java

Esta sem dúvida é uma das maiores novidades desta versão e algo bem aguardado, que já existia em outras linguagens como os Case Classes do Scala.

Apesar de já existirem alternativas no Java como o Lombok, que faz algo similar com o uso de anotações (@Data por exemplo), ter uma opção nativa é muito mais interessante.

Switch Expressions [JEP 361]

As expressões switch estavam em preview em versões anteriores e agora adquiriram seu estado final.

Para refrescar a memória, um switch clássico seria escrito desta maneira:

switch (day) {
case MONDAY:
case FRIDAY:
case SUNDAY:
System.out.println(6);
break;
case TUESDAY:
System.out.println(7);
break;
case THURSDAY:
case SATURDAY:
System.out.println(8);
break;
case WEDNESDAY:
System.out.println(9);
break;
}

Agora podemos escrever assim:

switch (day) {
case MONDAY, FRIDAY, SUNDAY -> System.out.println(6);
case TUESDAY -> System.out.println(7);
case THURSDAY, SATURDAY -> System.out.println(8);
case WEDNESDAY -> System.out.println(9);
}

Mais uma abordagem que ataca a verbosidade!

Mas não para aí, o switch deixou de ser um “statement” para se tornar uma “expression. Na prática, o código a seguir, apesar de usar a sintaxe do novo switch, também poderia ser escrito com o switch antigo:

static void howMany(int k) {
switch (k) {
case 1 -> System.out.println("one");
case 2 -> System.out.println("two");
default -> System.out.println("many");
}

}

Mas com a nova natureza de expressão agora podemos refatorar este método passando o switch como parâmetro do println, algo que antes não era possível:

static void howMany(int k) {
System.out.println(
switch (k) {
case 1 -> "one";
case 2 -> "two";
default -> "many";
}

);
}

Uma outra novidade do switch é “yield”. Ele foi criado para servir um valor quando um bloco inteiro de código for necessário. Por exemplo:

int j = switch (day) {
case MONDAY -> 0;
case TUESDAY -> 1;
default -> {
int k = day.toString().length();
int result = f(k);
yield result;
}

};

Como no “default” precisamos abrir parêntesis (criar um bloco), o yield se faz necessário para retornar o “result” este então é atribuído ao “j”.

Com esta mudança certamente teremos um aumento no uso do switch, que vinha sendo deixado de lado pela sua estrutura verbosa, datada, propensa a erros e de funcionalidade não muito diferenciada, tendo em vista outros controladores de fluxo.

Text Blocks (Second Preview) [JEP 368]

Por último temos o segundo preview dos “Text Blocks.

Frequentemente escrevemos Strings com quebras de linhas, principalmente por motivos de estética e boas práticas. Os blocos de texto oferecem uma abordagem mais adequada para estes casos:

Tomemos este código com diversos “+” para concatenação, e “\n” para quebra de linha:

String html = "<html>\n" +
" <body>\n" +
" <p>Hello, world</p>\n" +
" </body>\n" +
"</html>\n"
;

Agora utilizando a sintaxe dos blocos de texto:

String html = """
<html>
<body>
<p>Hello, world</p>
</body>
</html>
"""
;

E esta query (que já é poluída o suficiente só com os acentos graves):

String query = "SELECT `EMP_ID`, `LAST_NAME` FROM `EMPLOYEE_TB`\n" +
"WHERE `CITY` = 'INDIANAPOLIS'\n" +
"ORDER BY `EMP_ID`, `LAST_NAME`;\n"
;

Fica:

String query = """
SELECT `EMP_ID`, `LAST_NAME` FROM `EMPLOYEE_TB`
WHERE `CITY` = 'INDIANAPOLIS'
ORDER BY `EMP_ID`, `LAST_NAME`;
"""
;

Mas isso já existia desde o primeiro preview. A novidade do Java 14 é a introdução das duas novas sequências de escape:

  • \” no final da linha;
  • e “\s

O “\s” simplesmente se traduz para um espaço simples (\u0020). O “\s” pode ser usado tanto em blocos de texto como em Strings tradicionais.

Já o “\” no final da linha evita a quebra da mesma, exemplo:

String lorem = "Lorem ipsum dolor sit amet, consectetur " +
"adipiscing elit, sed do eiusmod tempor incididunt " +
"ut labore et dolore magna aliqua."
;

Poderá ser escrito agora:

String lorem = """
Lorem ipsum dolor sit amet, consectetur \
adipiscing elit, sed do eiusmod tempor incididunt \
ut labore et dolore magna aliqua.\
"""
;

Um último detalhe a se acrescentar é que blocos de texto podem ser usados em qualquer lugar que uma String tradicional pode ser usada, inclusive concatenando:

String code = "public void print(Object o) {" +
"""
System.out.println(Objects.toString(o));
}
""";

Entretanto esta prática pode prejudicar a legibilidade do código como já é perceptível. E piora se colocarmos uma variável no meio disso tudo.

Mas se mesmo assim você preferir usar esta mistura de sintaxes, há uma alternativa mais limpa:

String code = """
public void print($type o) {
System.out.println(Objects.toString(o));
}
""".replace("$type", type);

Utilizando o “replace”, “format” ou até mesmo “formatted” da classe String.

Conclusão

Este foi o meu top 5 das mudanças do Java 14!

Espero que este empenho em reduzir a característica verborrágica do Java continue, já que ela é tida como uma das grandes desvantagens da linguagem — mesmo que desde o Java 8 venha sendo mitigada em ritmo acelerado a cada novo release!

Para ver todas as outras 11 JEPs que não foram citadas neste artigo, acesse a página oficial.

Fontes:
https://openjdk.java.net/projects/jdk/14/
https://blogs.oracle.com/javamagazine/java-14-arrives-with-a-host-of-new-features
http://www.cs.williams.edu/FTfJP2011/6-Winther.pdf

Meu nome é Cayo Souza, sou Desenvolvedor Fullstack, entusiasta Java & Spring, apreciador de novas tecnologias e boas práticas de código. Acredito que o compartilhamento de conhecimento é a chave da evolução.

A Mobicare e a Akross combinam os Melhores Talentos, Tecnologias de Ponta, Práticas Agile e DevOps com Capacidades Operacionais avançadas para ajudar Operadoras Telecom e grandes empresas a gerarem novas receitas e a melhorarem a experiência dos seus próprios clientes.

Se você gosta de inovar, trabalhar com tecnologia de ponta e está sempre buscando conhecimento, somos um match perfeito!

Faça parte do nosso time. 😉

--

--

Cayo Souza
mobicareofficial

Senior Software Engineer @ iFood and addicted to online games