Atualizado: 28/09/2025

Este conteúdo é original e não foi gerado por inteligência artificial.

Pattern Matching com Records em Java

A partir do Java 21, é possível utilizar o Record Pattern, um recurso que permite não apenas verificar se um objeto é uma instância de um record, mas também desestruturá-lo — um processo que extrai os valores de seus componentes diretamente em variáveis. Este padrão pode ser aplicado tanto com o operador instanceof quanto com expressões switch.

O Operador instanceof

O exemplo a seguir demonstra o uso do Record Pattern com o operador instanceof.

public class Program {
    public static void main(String[] args) {
        Person tom = new Person("Tom", 22);

        if (tom instanceof Person(var pName, var pAge)) {
            System.out.printf("Person. Name: %s; Age: %d\n", pName, pAge);
        }
    }
}

record Person(String name, int age) {}

A expressão tom instanceof Person(var pName, var pAge) realiza duas operações. Primeiro, verifica se a variável tom é uma instância do record Person. Se for, ela extrai os valores dos componentes name e age e os atribui às novas variáveis de padrão pName e pAge, que ficam disponíveis dentro do escopo do if.

Também é possível declarar os tipos das variáveis de forma explícita, em vez de usar var.

if (tom instanceof Person(String pName, int pAge)) {
    System.out.printf("Person. Name: %s; Age: %d\n", pName, pAge);
}

Se algum componente do record não for necessário, ele pode ser ignorado na desestruturação, substituindo a variável por um sublinhado (_), conhecido como variável anônima. Por exemplo, se apenas o componente age for relevante:

if (tom instanceof Person(_, int pAge)) {
    System.out.printf("Person Age: %d\n", pAge);
}

Expressões switch

O Record Pattern pode ser utilizado de forma elegante em expressões switch, tornando o código mais conciso e legível.

Person tom = new Person("Tom", 22);

switch (tom) {
    case Person(var pName, var pAge) ->
        System.out.printf("Person. Name: %s; Age: %d\n", pName, pAge);
    case null ->
        System.out.println("Undefined");
}

Constantes e Cláusulas de Guarda (Guards)

É importante notar que o Record Pattern não permite o uso de valores constantes diretamente na desestruturação. O código a seguir, que tenta verificar se o nome da pessoa é "Tom" diretamente no padrão, resultará em um erro de compilação.

// Este código não compila!
if (tom instanceof Person("Tom", _)) { // Erro: "Tom" é uma constante
    System.out.println("Person Tom");
}

A abordagem correta para adicionar condições específicas é usar cláusulas de guarda. Com instanceof, isso é feito encadeando uma condição booleana com o operador &&.

public class Program {
    public static void main(String[] args) {
        Person tom = new Person("Tom", 22);
        Person bob = new Person("Bob", 46);

        printPerson(tom);   // Hello Tom
        printPerson(bob);   // Hello
    }

    static void printPerson(Person person) {
        if (person instanceof Person(var pName, _) && pName.equals("Tom")) {
             System.out.println("Hello Tom");
        } else if (person instanceof Person(_, _)) {
             System.out.println("Hello");
        }
    }
}

Dessa forma, a condição pName.equals("Tom") atua como uma cláusula de guarda. O primeiro bloco if só é executado se o objeto for um Person e seu nome for "Tom".

Ao usar switch, as cláusulas de guarda são introduzidas pela palavra chave when.

public class Program {
    public static void main(String[] args) {
        Person tom = new Person("Tom", 22);
        Person bob = new Person("Bob", 46);

        printPerson(tom);   // Hello Tom
        printPerson(bob);   // Hello
    }

    static void printPerson(Person person) {
        switch (person) {
            // Apenas para um Person cujo name é "Tom"
            case Person(var pName, _) when pName.equals("Tom") ->
                System.out.println("Hello Tom");

            // Para todos os outros objetos Person
            case Person(_, _) ->
                System.out.println("Hello");

            case null -> {} // Se for null, não faz nada
        }
    }
}

Padrões Aninhados

Os Record Patterns podem ser aninhados, permitindo desestruturar objetos complexos de forma declarativa e concisa.

public class Program {
    public static void main(String[] args) {
        Person tom = new Person("Tom", 41);
        Company mycorp = new Company("MyCorp", tom);

        if (mycorp instanceof Company(_, Person(var name, _))) {
            System.out.println("CEO: " + name); // CEO: Tom
        }
    }
}

record Person(String name, int age) {}
record Company(String name, Person ceo) {}

Neste exemplo, o segundo componente do record Company é um record Person. A expressão instanceof aninha outro padrão para desestruturar o record Person interno, extraindo o valor do componente name.

O mesmo pode ser feito com uma expressão switch.

public class Program {
    public static void main(String[] args) {
        Person tom = new Person("Tom", 41);
        Company mycorp = new Company("MyCorp", tom);

        switch (mycorp) {
            case Company(_, Person(var name, _)) ->
                System.out.println("CEO: " + name);
            case null -> {}
            default -> {}
        }
    }
}

record Person(String name, int age) {}
record Company(String name, Person ceo) {}

Ao usar switch com Pattern Matching, a verificação deve ser exaustiva, o que significa que todos os valores possíveis para a variável de entrada devem ser tratados por um dos case. No exemplo acima:

  1. case Company(_, Person(var name, _)) trata o caso em que mycorp é uma Company com um ceo não nulo.
  2. case null trata o caso em que a variável mycorp é nula.
  3. default é necessário para tratar todas as outras possibilidades, como o caso em que mycorp é uma instância de Company, mas seu componente ceo é nulo (new Company("MyCorp", null)). Este caso não corresponderia ao primeiro padrão.

Resumo

  • Record Pattern: Permite verificar se um objeto é uma instância de um record e, ao mesmo tempo, extrair os valores de seus componentes para variáveis locais.
  • Desestruturação: Simplifica o acesso aos dados de um record, eliminando a necessidade de chamar métodos de acesso.
  • Variável Anônima (_): É usada para ignorar componentes do record que não são necessários na lógica do programa.
  • Cláusulas de Guarda (when): Permitem adicionar condições lógicas aos padrões, refinando os critérios de correspondência.
  • Padrões Aninhados: Possibilitam a desestruturação de records que contêm outros records como componentes.
  • Exaustividade no switch: É obrigatório que os case de um switch com padrões cubram todas as possibilidades de valor da variável de entrada, incluindo null.
Política de Privacidade

Copyright © www.programicio.com Todos os direitos reservados

É proibida a reprodução do conteúdo desta página sem autorização prévia do autor.

Contato: programicio@gmail.com