Com o lançamento do Java 21, os desenvolvedores têm à disposição recursos poderosos que transformam a maneira como escrevemos o código Java. Dois desses recursos são o Pattern Matching e os Java Records. Vamos explorar como eles trabalham juntos para simplificar nossa vida.
Record Patterns: Simplificando a Manipulação de Dados
Com os Record Patterns, desestruturar um Record tornou-se uma tarefa trivial. Vamos aplicar isso a uma hierarquia de empregados usando interfaces.
sealed interface Empregado permits EmpregadoAssalariado, Freelancer{
String nome();
}
record EmpregadoAssalariado(String nome, double salario) implements Empregado{}
record Freelancer(String nome, double pagamentoPorHora, int horasTrabalhadas) implements Empregado {}
Antes do Java 21
Tradicionalmente, você poderia usar uma série de if-else ou um switch sem Pattern Matching:
Para processar esses empregados, teríamos que usar instanceof e casting:
void processarPagamento(Empregado empregado) {
if (empregado instanceof EmpregadoAssalariado assalariado) {
System.out.println(assalariado.nome() + " ganha " + assalariado.salario());
} else if (empregado instanceof Freelancer freelancer) {
System.out.println(freelancer.nome() + " faturará " + (freelancer.pagamentoPorHora() * freelancer.horasTrabalhadas()));
}
}
Com Record Patterns
Agora, com Record Patterns, a mesma função se torna mais limpa e direta:
void processarPagamento(Empregado empregado) {
if (empregado instanceof EmpregadoAssalariado(String nome, double salario)) {
System.out.println(nome + " ganha " + salario);
} else if (empregado instanceof Freelancer(String nome, double pagamentoPorHora, int horas)) {
System.out.println(nome + " faturará " + (pagamentoPorHora * horas));
}
}
Com Pattern Matching no Switch
Agora com o Java 21, podemos simplificar o método usando Pattern Matching no switch:
void processarPagamento(Empregado empregado) {
switch (empregado) {
case EmpregadoAssalariado(String nome, double salario) -> System.out.println(nome + " ganha " + salario);
case Freelancer(String nome, double pagamentoPorHora, int horasTrabalhadas) ->
System.out.println(nome + " faturará " + (pagamentoPorHora * horasTrabalhadas));
}
}
No Java, os sealed types garantem em tempo de compilação que apenas um conjunto fixo de subtipos é possível. Se o switch estiver lidando com um sealed type e todos os subtipos possíveis forem cobertos, o compilador pode garantir que um default não é necessário.
Unnamed Pattern – Preview
O underscore(_) pode ser utilizado como uma variável de descarte em expressões de Pattern Matching, incluindo o Pattern Matching no switch. O uso do underscore nesse contexto permite aos desenvolvedores ignorar certos valores que não são de interesse para a lógica subsequente. Aqui está um exemplo revisado que destaca essa utilização:
void processarPagamento (Empregado empregado) {
switch (empregado) {
case EmpregadoAssalariado(String _, double salario) ->
System.out.println("Salário: " + salario);
case Freelancer(String _, double pagamentoPorHora, int horas) ->
System.out.println("Pagamento estimado: " + (pagamentoPorHora * horas));
}
}
Neste caso, estamos interessados apenas nos valores numéricos que definem o salário e o pagamento do freelancer, e não nos nomes associados a esses registros.
Conclusão
Com a introdução do Record Patterns e Pattern Matching em Java 21, a forma como interagimos com diferentes tipos e interfaces torna-se mais intuitiva e menos propensa a erros. O código fica mais limpo, a manutenção mais fácil, e a expressividade atinge novos patamares. Adotar essas novas funcionalidades significa escrever um código Java mais moderno e eficiente.
Referências
Posts Relacionados
Descubra como os Virtual Threads estão moldando o futuro da concorrência em Java 21 em nosso último post sobre Virtual Threads. Mergulhe nos detalhes agora!
Pingback: Java 21: Codifique com Emoção Usando o Novo Suporte a Emojis - A Arte de Programar