[PT-BR] Pattern Matching com instanceof
Tags: java, ptbr
Published: 2020-10-22 13:34:59

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`.

```java
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
```

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. 

```java
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
```
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.

```java
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();
}
```
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

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

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!