W3docs

Correspondência de Padrões em Java

Use correspondência de padrões em Java para instanceof e switch — padrões de tipo, padrões de record e desconstrução.

Durante anos, o código Java que trabalhava com valores de tipo desconhecido seguia um ritual tedioso: testar o tipo com instanceof, depois converter para esse tipo e então utilizá-lo. A correspondência de padrões colapsa esse ritual em uma única expressão. Um padrão descreve a forma dos dados; se um valor corresponde, o Java vincula suas partes a variáveis que podem ser usadas imediatamente — sem conversão manual.

A correspondência de padrões chegou em etapas: primeiro os padrões de instanceof, depois padrões em switch, e então os padrões de record que desconstroem records em seus componentes. Juntos, permitem escrever código declarativo e seguro em relação a tipos, que se lê como os dados com que opera.

Este capítulo cobre o padrão de instanceof, padrões de tipo em switch, padrões com guarda e tratamento de null, e padrões de record — depois os une em um programa executável. Ele se baseia em três recursos que convém revisar antes: o operador instanceof, records e expressões switch.

Correspondência de Padrões para instanceof

O padrão clássico de testar e converter precisava de três referências ao mesmo tipo. O padrão de instanceof vincula uma variável ao mesmo tempo que realiza o teste, e o vínculo está em escopo em qualquer lugar em que o teste é comprovadamente verdadeiro.

Object value = "hello";

// Old way: test, then cast
if (value instanceof String) {
    String s = (String) value;
    System.out.println(s.length());
}

// Pattern way: test and bind together
if (value instanceof String s) {
    System.out.println(s.length());
}

Como a variável de vínculo participa da expressão booleana, você pode continuar restringindo no mesmo if. O compilador prova que s é seguro de usar:

if (value instanceof String s && s.length() > 3) {
    System.out.println(s.toUpperCase());
}

Padrões em switch

Um switch pode corresponder a padrões de tipo, despachando pelo tipo em tempo de execução do seletor. Cada case vincula o valor correspondido, de modo que o corpo trabalha diretamente com uma variável tipada. Isso transforma longas cadeias de if/else instanceof em uma tabela compacta e legível.

static String format(Object value) {
    return switch (value) {
        case Integer i -> "int: " + i;
        case Long l    -> "long: " + l;
        case String s  -> "string: " + s;
        default        -> "other: " + value;
    };
}

Um switch com padrão de tipo deve ser exaustivo — precisa cobrir todas as entradas possíveis. Para seletores Object arbitrários, isso significa um ramo default; para hierarquias sealed, o compilador conhece o conjunto completo de subtipos e pode verificar a exaustividade sem um default.

Padrões com Guarda e null

Uma cláusula when adiciona uma condição booleana a um case, permitindo que dois valores do mesmo tipo tomem ramos diferentes. Isso é chamado de padrão com guarda, e a ordem importa: cases com guarda mais específicos vêm antes do fallback sem guarda.

static String size(String s) {
    return switch (s) {
        case String t when t.isEmpty() -> "empty";
        case String t when t.length() < 5 -> "short";
        case String t -> "long (" + t.length() + ")";
    };
}

Tradicionalmente, um switch lançava NullPointerException com um seletor null. Um switch com padrão pode tratar null explicitamente com um case null, mantendo a verificação de nulo dentro do mesmo construto em vez de uma guarda separada antes dele.

RecursoSintaxePropósito
Padrão de tipocase String sCorresponder por tipo e vincular
Padrão com guardacase String s when s.isEmpty()Adicionar uma condição a um case
Rótulo nullcase nullCorresponder a um seletor null
Padrão de recordcase Point(int x, int y)Desconstruir um record

Padrões de Record

Um padrão de record corresponde a um record e vincula seus componentes em uma só operação, eliminando as chamadas de acessor. Como os records expõem seus componentes, o compilador conhece a forma exata e permite nomear cada parte inline. Padrões de record são aninhados, portanto é possível desestruturar um record de records.

record Point(int x, int y) {}
record Line(Point start, Point end) {}

static String render(Object o) {
    return switch (o) {
        case Point(int x, int y) -> "point " + x + "," + y;
        // Nested: pull both endpoints' coordinates out at once
        case Line(Point(int x1, int y1), Point(int x2, int y2)) ->
            "line " + x1 + "," + y1 + " -> " + x2 + "," + y2;
        default -> "unknown";
    };
}

A correspondência de padrões brilha com tipos sealed: quando uma interface lista suas implementações permitidas, um switch sobre elas é exaustivo sem um default, e adicionar um novo subtipo transforma o case ausente em um erro de compilação em vez de um bug silencioso.

Um Exemplo Completo e Executável

O programa abaixo une as peças. Ele usa um padrão de instanceof com guarda, uma hierarquia Shape sealed de records, padrões de record que desconstroem cada forma em um switch, um padrão com guarda que identifica um quadrado, e um case null — tudo sem um único cast explícito.

java— editable, runs on the server

O que observar na execução:

  • describe(42) imprime positive int 42 porque a guarda instanceof Integer i && i > 0 testa o tipo e o valor juntos antes de vincular i.
  • describe(-5) cai em unknown — o mesmo padrão Integer corresponde ao tipo, mas a guarda i > 0 falha, mostrando como uma guarda refina um padrão de tipo.
  • O switch de area não precisa de default: Shape é sealed, portanto listar Circle, Rectangle e Triangle é exaustivo e o compilador fica satisfeito.
  • O retângulo 5.0 x 5.0 é impresso como square side=5.0 porque seu case com guarda when w == h está posicionado antes do case geral Rectangle r e tem precedência.
  • A linha final imprime no shape: o ramo case null trata um seletor null dentro do switch em vez de lançar NullPointerException.

Prática

Prática
Em um switch com padrão, o que a adição de uma cláusula 'when' a um case faz?
Em um switch com padrão, o que a adição de uma cláusula 'when' a um case faz?
Was this page helpful?