DEV Community

João Victor Martins
João Victor Martins

Posted on

[PT-BR] Pattern Matching com instanceof

O Java sempre foi considerado uma linguagem verbosa. Muitas linhas de códigos são escritas para realizar tarefas simples. Apesar de ser uma plataforma de ponta desde os primórdios, esta característica sempre o perseguiu. Na JDK 10 foi incluída uma JEP (JDK Enhancement Proposals), de número 322, que informa uma mudança no lançamento de releases. Novas versões serão lançadas a cada 6 meses. Um dos motivos para tal ação é a melhoria contínua, pois fornece features em menor tempo e recebe-se feedback constante da comunidade. As mudanças são feitas na plataforma em geral, como na JDK, Garbage Collector e na própria linguagem. Falando nas alterações na linguagem, percebe-se uma preocupação com a tal da verbosidade e a JEP 375 é um belo exemplo disso. A JEP em questão é a Pattern Matching for instanceof, que aprimora a linguagem com pattern matching.

Mas afinal, o que é Pattern Matching?

Pattern Matching é uma técnica, que foi adaptada para muitos estilos diferentes de linguagens de programação, desde a década de 1960, incluindo linguagens orientadas a texto como SNOBOL4 e AWK, linguagens funcionais como Haskell e ML, e mais recentemente estendida para linguagens orientadas a objetos como Scala (e mais recentemente, C #).

(https://cr.openjdk.java.net/~briangoetz/amber/pattern-match.html)

Pattern Matching permite que a "forma" desejada de um objeto seja expressa de forma concisa (the Patthern) e que várias instruções e expressões testem essa "forma" em relação à entrada (Matching).

(https://openjdk.java.net/jeps/375)

Motivação

Todo desenvolvedor já precisou escrever alguma lógica que verifica se uma expressão tem um determinado tipo e, caso seja verdade, extrair alguma informação para processamento posterior. Um exemplo disso é uma operação com o instanceof.

if(conta instanceof ContaCorrente) {
     ContaCorrente contaCorrente = (ContaCorrente) conta;
     System.out.println(contaCorrente.mostrarExtrato());
} else if(conta instanceof ContaPoupanca) {
     ContaPoupanca contaPoupanca = (ContaPoupanca) conta;
     System.out.println(contaPoupanca.mostrarExtrato());
}
// Demais else if's que poderão existir
Enter fullscreen mode Exit fullscreen mode

O código acima é funcional, porém existem alguns pontos de atenção.

  • Repetição de código: Usa-se ContaCorrente e ContaPoupanca três vezes em duas linhas de código.
  • Casting: Podem esconder erros (bugs) que não serão verificados em tempo de compilação.
  • Baixa coesão: Repetições e castings atrapalham a leitura e consequentemente o entendimento do trecho de código.

Pattern Matching for instanceof

O operador instanceof é estendido para obter um padrão de teste de tipo, em vez de apenas um tipo.

if(conta instanceof ContaCorrente contaCorrente) {
     System.out.println(contaCorrente.mostrarExtrato());
} else if(conta instanceof ContaPoupanca contaPoupanca) {
     System.out.println(contaPoupanca.mostrarExtrato());
}
// Demais else if's que poderão existir
Enter fullscreen mode Exit fullscreen mode

Em relação ao exemplo anterior, o processamento difere em alguns pontos. O operador "combina" o objeto de destino com o padrão de teste de tipo da seguinte maneira. Se conta for uma instância de ContaCorrente, é feito o casting (implícito) para a variável contaCorrente e esta variável estará no escopo do bloco.

É importante ressaltar, que caso exista um else, a variável não fará parte de seu escopo.

if(objeto instanceof String string) {
     // string está no escopo do bloco true
     string.toLowerCase();
} else {
     // string NÃO está no escopo do bloco false
     string.toString();
}
Enter fullscreen mode Exit fullscreen mode

Percebe-se que os pontos de atenção do exemplo anterior já não ocorrem mais.

Futuro

Ainda que haja uma melhora significativa na forma de escrever o código acima, conforme há necessidade de verificar novos tipos de contas, a legibilidade fica comprometida. Pensando nisso, uma futura JEP prevê uma melhora com pattern matchings para switch e o código a seguir será possível

switch(conta) {
     case ContaCorrente cc -> cc.numero = 12345;
     case ContaPoupanca cp -> cp.numero = 12345;
     // Demais cases que poderão existir
}
Enter fullscreen mode Exit fullscreen mode

O mais interessante dessa abordagem é que não é necessário o default, porque deve-se garantir que todas as comparações estão sendo feitas nos cases (Deve-se verificar todos os filhos de Conta) e caso uma ou mais comparações não sejam feitas, haverá um erro de compilação.

Conclusão

Percebe-se que os engenheiros e desenvolvedores da linguagem tem se preocupado com a questão da verbosidade da linguagem. Com o lançamento de releases de 6 em 6 meses, uma melhora rápida pode ser sentida. A ideia do post era mostrar uma dessas melhoras e um plano para o futuro. Se algum dos pontos não ficou claro, estou aberto a dúvidas e/ou sugestões. Até a próxima!

Top comments (0)