CodeGym /Cursos /JAVA 25 SELF /Pattern Matching no switch (Java 17/21+)

Pattern Matching no switch (Java 17/21+)

JAVA 25 SELF
Nível 65 , Lição 2
Disponível

1. Switch clássico: limitações do passado

Vamos relembrar como era o switch no Java antes do pattern matching. Ele era, sejamos honestos, bastante conservador. Funcionava apenas com primitivos (int, char, e um pouco depois enum, String), e, se você quisesse tratar diferentes tipos de objetos, precisava escrever longas cadeias de if-else.

Object obj = ...;
if (obj instanceof String) {
    String s = (String) obj;
    System.out.println("String: " + s);
} else if (obj instanceof Integer) {
    Integer i = (Integer) obj;
    System.out.println("Número: " + i);
} else if (obj == null) {
    System.out.println("null!");
} else {
    System.out.println("Algo diferente");
}

Confortável? Nem tanto. O código é verboso, fácil de errar e, se surgir um novo tipo, você terá que reescrever a cadeia de if-else.

2. Pattern Matching no switch: revolução no Java 17/21+

O Java 17 (preview) e, de forma estável, o Java 21+ apresentaram uma nova versão do switch que suporta pattern matching. Agora o switch pode trabalhar não apenas com primitivos, mas com quaisquer objetos, tipos, até mesmo com null, e suporta condições adicionais (expressões de guarda).

Nova sintaxe: concisão e poder

Exemplo simples de pattern matching em switch:

Object obj = ...;
switch (obj) {
    case String s -> System.out.println("String: " + s);
    case Integer i -> System.out.println("Número: " + i);
    case null -> System.out.println("null!");
    default -> System.out.println("Algo diferente");
}

O que está acontecendo aqui?

  • Se obj é uma string, a variável s é automaticamente convertida para o tipo String.
  • Se obj é um inteiro, a variável i torna-se Integer.
  • Se obj é null, dispara uma ramificação específica.
  • Todo o resto — default.

Por que isso é conveniente?

  • Sem casting manual: o Java cuida da segurança de tipos.
  • Menos código: nada de parênteses extras, casts e duplicação.
  • Mais seguro: o compilador verifica se você não esqueceu de tratar os casos possíveis (especialmente com classes sealed, sobre as quais falaremos adiante).
  • Fácil adicionar novos tipos: basta acrescentar mais uma ramificação case.

3. Pattern Matching no switch: exemplos básicos

Exemplo 1: Tratando diferentes tipos

Object obj = 42;

switch (obj) {
    case String s -> System.out.println("String: " + s);
    case Integer i -> System.out.println("Número: " + i);
    case null -> System.out.println("É null");
    default -> System.out.println("Tipo desconhecido");
}

Saída:

Número: 42

Exemplo 2: Usando expressões de guarda (when)

Às vezes, não basta conhecer o tipo — você quer adicionar uma condição extra.

Object obj = "Hello, world!";

switch (obj) {
    case String s when s.length() > 10 -> System.out.println("String longa: " + s);
    case String s -> System.out.println("String curta: " + s);
    case Integer i when i > 0 -> System.out.println("Número positivo: " + i);
    case Integer i -> System.out.println("Número não positivo: " + i);
    default -> System.out.println("Algo diferente");
}

Saída:

String longa: Hello, world!

Observe:

  • when é uma expressão de guarda, uma verificação adicional para o case.
  • Se a condição não for satisfeita, os próximos case são verificados.

Exemplo 3: Tratamento de null

Object obj = null;

switch (obj) {
    case null -> System.out.println("É null!");
    default -> System.out.println("Não é null");
}

Saída:

É null!

4. Pattern Matching no switch com classes e herança

O pattern matching em switch funciona especialmente bem com hierarquias de classes.

sealed interface Shape permits Circle, Rectangle, Square {}

final class Circle implements Shape {
    final double radius;
    Circle(double radius) { this.radius = radius; }
}

final class Rectangle implements Shape {
    final double width, height;
    Rectangle(double width, double height) { this.width = width; this.height = height; }
}

final class Square implements Shape {
    final double side;
    Square(double side) { this.side = side; }
}
Shape shape = new Rectangle(2, 3);

String description = switch (shape) {
    case Circle c -> "Circunferência com raio " + c.radius;
    case Rectangle r -> "Retângulo " + r.width + "x" + r.height;
    case Square s -> "Quadrado com lado " + s.side;
};
System.out.println(description);

Saída:

Retângulo 2.0x3.0

Dica: Se você usa classes sealed e tratou todas as suas variantes, a ramificação default não é necessária — o compilador entende que não há outros casos possíveis!

5. Usando switch com classes record e padrões aninhados

O pattern matching no switch suporta trabalhar com classes record e padrões aninhados (a partir do Java 21+).

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

Object obj = new Line(new Point(1, 2), new Point(3, 4));

String result = switch (obj) {
    case Line(Point(int x1, int y1), Point(int x2, int y2)) ->
        "Linha de (" + x1 + "," + y1 + ") até (" + x2 + "," + y2 + ")";
    case Point(int x, int y) -> "Ponto (" + x + "," + y + ")";
    default -> "Objeto desconhecido";
};
System.out.println(result);

Saída:

Linha de (1,2) até (3,4)

6. Particularidades e limitações do pattern matching no switch

Variáveis disponíveis apenas dentro de case

A variável declarada no padrão (por exemplo, String s) existe apenas dentro do respectivo bloco case. Fora dele, ela não está disponível.

switch (obj) {
    case String s -> System.out.println(s); // aqui s é visível
    // System.out.println(s); // aqui há erro de compilação!
}

Tratamento de null

  • No antigo switch com objetos, tentar passar null levava a NullPointerException.
  • No novo pattern matching switch, é possível tratar explicitamente null via case null.

Expressões de guarda (when)

Condições adicionais (when) estão disponíveis apenas no switch em estilo de expressão (sintaxe com seta ->).

case String s when s.isEmpty() -> System.out.println("String vazia");

Requisitos para JDK e IDE

  • O pattern matching no switch é um recurso do Java 17 (preview) e Java 21+ (estável).
  • Para funcionar, é necessário o JDK 21 e uma IDE que suporte os novos recursos (por exemplo, IntelliJ IDEA 2023+).

7. Aplicando o pattern matching switch em um aplicativo real

Vamos considerar um aplicativo didático — um rastreador de tarefas simples. Suponha que você tenha um tipo de tarefa abstrato e diferentes variações:

sealed interface Task permits SimpleTask, DeadlineTask, MeetingTask {}

final class SimpleTask implements Task {
    final String description;
    SimpleTask(String description) { this.description = description; }
}

final class DeadlineTask implements Task {
    final String description;
    final java.time.LocalDate deadline;
    DeadlineTask(String description, java.time.LocalDate deadline) {
        this.description = description;
        this.deadline = deadline;
    }
}

final class MeetingTask implements Task {
    final String topic;
    final java.time.LocalTime time;
    MeetingTask(String topic, java.time.LocalTime time) {
        this.topic = topic;
        this.time = time;
    }
}

Toda a lógica em um único switch compacto:

Task task = new DeadlineTask("Entregar o projeto", java.time.LocalDate.of(2024, 7, 1));

String info = switch (task) {
    case SimpleTask st -> "Tarefa simples: " + st.description;
    case DeadlineTask dt -> "Tarefa com prazo: " + dt.description + ", até " + dt.deadline;
    case MeetingTask mt -> "Reunião: " + mt.topic + " às " + mt.time;
};
System.out.println(info);

Saída:

Tarefa com prazo: Entregar o projeto, até 2024-07-01

8. Erros comuns ao usar pattern matching no switch

Erro nº 1: esqueceu de tratar null. Se você não adicionar case null e obj puder ser null, receberá MatchException (ou NullPointerException nos switch antigos). Sempre trate null explicitamente, quando possível.

Erro nº 2: não considerou todos os herdeiros de uma classe sealed. Se você usa uma classe sealed e não tratou todas as variantes no switch, o compilador emitirá um erro ou exigirá a ramificação default. Não se esqueça de adicionar novos case ao expandir a hierarquia.

Erro nº 3: variável de padrão indisponível fora do bloco case. A variável declarada no padrão existe apenas dentro do bloco com seta. Tentar usá-la fora desse bloco causará um erro de compilação.

Erro nº 4: usar recursos novos em JDK ou IDE antigas. O pattern matching no switch requer Java 21+ e suporte correspondente na sua IDE. Se usar uma versão antiga, você terá erro de compilação ou sugestões incorretas.

Erro nº 5: confusão com expressões de guarda. Tentando usar when no switch em estilo de bloco tradicional? Não vai funcionar. As guardas estão disponíveis apenas para a sintaxe com seta ->.

Erro nº 6: esqueceu a ramificação default ao trabalhar com hierarquias abertas. Se o seu switch não trabalha apenas com classes sealed, inclua obrigatoriamente default — do contrário, nem todos os casos serão tratados.

Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION