Novidades JDK 14

Jeferson Francisco
Releases Java
Published in
5 min readMar 11, 2020

17/03/2020 é o dia do lançamento da versão final do Java 14. Neste post vamos descrever as principais novidades desta versão.

O Java 14 contém mais recursos novos do que os dois lançamentos anteriores — a maioria deles visando facilitar a codificação.

Novos recursos

A versão 14 inclui mais JEPs (Java Enhancement Proposals) que Java 12 e 13 combinados. Então o que é mais relevante para os desenvolvedores Java que escrevem e mantêm código diariamente?

Principais mudanças

  • Expressões de switch finalizadas. Elas apareceram pela primeira vez no Java 12 e Java 13 como preview e agora fazem parte integral do Java 14.
  • Correspondência de padrões para instanceof (um recurso da linguagem)
  • NullPointerExceptions úteis (um recurso da JVM)

Expressões Switch

No Java 14, as expressões do switch se tornam permanentes. Em versões anteriores, a expressão do switch era uma preview feature. Como lembrete, as features são designadas como preview para obter feedback e é possível que elas sejam alterados ou até removidas com base no feedback; mas espera-se que a maioria acabe se tornando permanente em Java.

Os benefícios das novas expressões de switch incluem escopo reduzido para bugs devido à ausência de comportamento indireto, exaustividade e facilidade de escrita, graças à expressão e à forma composta.

var log = switch (event) {
case PLAY -> "User has triggered the play button";
case STOP, PAUSE -> "User needs a break";
default -> {
String message = event.toString();
LocalDateTime now = LocalDateTime.now();
yield "Unknown event " + message +
" logged on " + now;
}
};

Blocos de Texto (Text Blocks)

O Java 13 introduziu blocos de texto como um recurso de visualização. Essa feature está passando por uma segunda rodada de visualização com o Java 14 e incorpora alguns ajustes. É bastante comum escrever códigos com muitas concatenações de sequências de caracteres e sequências de escape, para fornecer uma formatação de texto multilinha adequada. O código abaixo mostra um exemplo de formatação HTML:

String html = "<HTML>" +
"\n\t" + "<BODY>" +
"\n\t\t" + "<H1>\"Java 14 is here!\"</H1>" +
"\n\t" + "</BODY>" +
"\n" + "</HTML>";

Com blocos de texto, você pode simplificar esse processo e escrever um código mais elegante usando as três aspas que delimitam o início e o final de um bloco de texto:

String html = """
<HTML>
<BODY>
<H1>"Java 14 is here!"</H1>
</BODY>
</HTML>""";

Correspondência de padrões para instanceof (Pattern Matching)

Nessa versão foi inserido uma nova preview feature com o intuito de eliminar casts explícitos após validar uma condição com instanceof. Veja a diferença de implementação antes da versão 14:

if (obj instanceof Group) {
Group group = (Group) obj;

// use group specific methods
var entries = group.getEntries();
}

Agora veja esse mesmo trecho de código refatorado para utilizar a feature:

if (obj instanceof Group group) {
var entries = group.getEntries();
}

Se estamos fazendo uma validação para assegurar que obj é do tipo Group porque devemos dizer que obj é uma instância de Group novamente na primeira linha? Isto aumenta exponencialmente o escopo para erros.

Uma pesquisa realizada em 2011 relacionada a esta preview reportou que 24% de todos os casts são realizados após uma condição utilizando instanceof. Este pode ser apenas o começo, podemos esperar novos recursos para reduzir verbosidade, e assim, reduzir a probabilidade de erros.

Registros (records)

Há mais preview nessa versão: records. Esta feature segue a mesma tendência das outras mencionadas que é reduzir verbosidade e auxiliar os desenvolvedores a escreverem códigos mais concisos. O foco de um record é armazenar dados nos campos declarados de uma classe de domínio sem adicionar comportamentos customizados. Em outras palavras imagine uma classe imutável com alguns campos declarados sem nenhuma ação, apenas a implementação do método construtor, getters, toString() e hashCode.

Normalmente nossas IDEs geram códigos automaticamente tomando espaço e diminuindo a legibilidade. Veja abaixo a implementação da classe BankTransaction:

public class BankTransaction {
private final LocalDate date;
private final double amount;
private final String description;


public BankTransaction(final LocalDate date,
final double amount,
final String description) {
this.date = date;
this.amount = amount;
this.description = description;
}

public LocalDate date() {
return date;
}

public double amount() {
return amount;
}

public String description() {
return description;
}

@Override
public String toString() {
return "BankTransaction{" +
"date=" + date +
", amount=" + amount +
", description='" + description + '\'' +
'}';
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
BankTransaction that = (BankTransaction) o;
return Double.compare(that.amount, amount) == 0 &&
date.equals(that.date) &&
description.equals(that.description);
}

@Override
public int hashCode() {
return Objects.hash(date, amount, description);
}
}

Com a nova feature reduzimos drasticamente a verbosidade armazenando os dados de forma clara e objetiva, porém contendo a implementação de hashCode, toString() e equals() e os getters. Refatorando a classe BankTransaction com record:

public record BankTransaction(LocalDate date,
double amount,
String description) {}

Os campos declarados são implicitamente atribuídos como final, Isso não quer dizer que objetos declarados dentro do record são imutáveis.

NullPointerExceptions que ajudam

Sem dúvidas é o erro que mais atormenta os desenvolvedores Java. O erro é lançado quando um objeto com valor nulo está sendo acessado. Por mais que o stack trace lhe forneça a linha do erro, na maioria das vezes é necessário realizar debug na aplicação para identificar o objeto nulo. Veja o código abaixo:

var name = user.getLocation().getCity().getName();

Antes do Java 14 você teria o seguinte erro:

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

No código há vários métodos sendo chamados e qualquer um deles pode estar com o valor nulo incluindo a variável user. No Java 14 você tem a opção de receber um diagnóstico mais preciso:

Exception in thread "main" java.lang.NullPointerException: Cannot invoke "Location.getCity()" because the return value of "User.getLocation()" is null
at NullPointerExample.main(NullPointerExample.java:5)

Nesta nova mensagem temos 2 componentes claros:

  • A consequência: Location.getCity() não pode ser chamado
  • O motivo: O valor de retorno de User.getLocation() é nulo

Para habilitar esse diagnóstico mais rico é necessário rodar sua aplicação com uma nova flag:

-XX:+ShowCodeDetailsInExceptionMessages

Exemplo:

java -XX:+ShowCodeDetailsInExceptionMessages NullPointerExample

Essa mensagem mais detalhada não funciona apenas em chamadas de métodos mas em todos os locais passíveis desse erro, como: acesso a campos, acesso a arrays e atribuição de valores.

Essa melhoria na mensagem estará disponível por default em versões futuras do Java conforme reportado aqui.

E essas são as mudanças mais relevantes para os desenvolvedores Java, porém a versão conta com algumas depreciações, remoção de pacotes e algumas mudanças internas. Ficou curioso ? Acesse a página oficial e confira todas as novidades.

Fontes:

https://blogs.oracle.com/javamagazine/java-14-arrives-with-a-host-of-new-features

https://openjdk.java.net/projects/jdk/14/

--

--