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.
GO TO FULL VERSION