Expressões switch Modernas em Java
Use expressões switch modernas em Java com rótulos de seta, yield, exaustividade e correspondência de padrões.
A instrução switch tradicional faz parte do Java desde a versão 1.0, mas carregava muito peso: bugs de fall-through, instruções break repetitivas e nenhuma forma de produzir um valor. As expressões switch, finalizadas no Java 14, resolvem tudo isso. Elas transformam o switch de uma instrução de controle de fluxo desajeitada em uma expressão concisa que produz valores.
Este capítulo percorre o switch moderno: rótulos de seta, a palavra-chave yield, casos com múltiplos rótulos, verificação de exaustividade e exatamente como a nova forma difere da instrução que você já pode conhecer.
De Instrução a Expressão
A instrução switch clássica executa efeitos colaterais e depende de break para impedir o fall-through. Esqueça um break e a execução cai silenciosamente no próximo caso — uma fonte notória de bugs.
// Traditional switch statement (error-prone)
String kind;
switch (day) {
case SATURDAY:
case SUNDAY:
kind = "weekend";
break; // forget this and you fall through
default:
kind = "weekday";
}Uma expressão switch reduz isso a uma única atribuição. A forma com seta (->) nunca cai em fall-through, portanto nenhum break é necessário.
// Modern switch expression
String kind = switch (day) {
case SATURDAY, SUNDAY -> "weekend";
default -> "weekday";
};Todo o switch agora avalia para um valor, que você pode atribuir, retornar ou passar como argumento diretamente.
Rótulos de Seta e Casos com Múltiplos Rótulos
O rótulo de seta case L -> associa um rótulo (ou vários, separados por vírgula) a uma única ação. Apenas o ramo correspondente é executado — não há fall-through com o qual se preocupar.
int numLetters = switch (month) {
case JANUARY, JUNE, JULY -> 4;
case FEBRUARY, MARCH, APRIL, MAY -> 5;
case SEPTEMBER, OCTOBER, NOVEMBER, DECEMBER -> switchOnLength(month);
case AUGUST -> 6;
};Agrupar rótulos com vírgulas substitui o antigo truque de empilhar linhas case vazias para compartilhar um corpo, e lê-se com muito mais clareza.
| Recurso | switch tradicional (case L:) | switch moderno (case L ->) |
|---|---|---|
| Fall-through | Sim, a menos que você use break | Não, cada ramo é isolado |
| Produz um valor | Não | Sim (é uma expressão) |
| Múltiplos rótulos | Linhas case vazias empilhadas | Separados por vírgula em uma linha |
| Escopo de variáveis | Compartilhado em todo o bloco | Local a cada bloco de ramo |
Blocos e a Palavra-chave yield
Quando um ramo precisa de mais do que uma única expressão, use um bloco { ... } e retorne seu valor com yield. A palavra-chave yield é para uma expressão switch o que return é para um método: ela fornece o valor que o ramo produz.
int gradePoints = switch (grade) {
case 'A' -> 4;
case 'B' -> 3;
default -> {
log("Unknown grade: " + grade);
yield 0; // the value this branch evaluates to
}
};Você ainda pode usar rótulos de dois pontos com yield se preferir a sintaxe antiga, mas a forma com seta é a escolha idiomática moderna e evita completamente o fall-through acidental.
Exaustividade e default
Uma expressão switch deve ser exaustiva: toda entrada possível deve ser tratada, pois a expressão precisa produzir um valor independentemente do que aconteça. Para a maioria dos tipos, isso é satisfeito com um ramo default. Para um enum, o compilador pode verificar a exaustividade diretamente — se você cobrir todas as constantes, default se torna opcional.
// No default needed: all enum constants are covered,
// so the compiler knows the switch is exhaustive.
boolean isWeekend = switch (day) {
case SATURDAY, SUNDAY -> true;
case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> false;
};Se você omitir uma constante e não houver default, o código não compilará. Essa rede de segurança em tempo de compilação é um dos maiores ganhos práticos em relação à instrução antiga, que silenciosamente deixava uma variável sem atribuição.
Correspondência de Padrões em switch
Versões recentes do Java estendem o switch para que cada case possa corresponder ao tipo do valor, não apenas a uma constante. Isso é a correspondência de padrões para switch (uma prévia no Java 17–20, finalizada no Java 21). Em vez de escrever uma cadeia de verificações instanceof e conversões, você rotula cada ramo com um padrão de tipo e vincula uma variável na mesma etapa.
static String describe(Object obj) {
return switch (obj) {
case Integer i -> "int " + i; // matches and binds i
case String s -> "string of length " + s.length();
case null -> "nothing"; // null can be its own label
default -> "something else";
};
}A correspondência de padrões é especialmente poderosa com records e tipos selados: quando um switch cobre todos os subtipos permitidos de um tipo selado, o compilador o trata como exaustivo, portanto você pode eliminar o default completamente.
sealed interface Shape permits Circle, Square {}
record Circle(double radius) implements Shape {}
record Square(double side) implements Shape {}
static double area(Shape shape) {
return switch (shape) { // no default: all permitted types covered
case Circle c -> Math.PI * c.radius() * c.radius();
case Square s -> s.side() * s.side();
};
}switch só podia testar um enum, um tipo integral ou uma String. Os padrões de tipo permitem que o switch funcione em qualquer tipo de referência, tornando-o um substituto natural para longas escalas if/else if.Um Exemplo Completo Desenvolvido
O programa abaixo reúne as peças: rótulos de seta com casos de múltiplos rótulos, um bloco yield para um ramo computado, cobertura exaustiva de enum e uma expressão switch atribuída diretamente a uma variável.
O que extrair da execução:
kind()retorna o resultado de uma expressão switch diretamente —MONDAYeFRIDAYimprimemweekday,SATURDAYimprimeweekend, tudo sem um únicobreak.- Os dois ramos de seta em
kind()cobrem todas as sete constantes do enum, portanto o switch é exaustivo e não precisa dedefault. - Em
letterGrade(), as pontuações 95, 83, 71 e 64 mapeiam claramente através de rótulos de seta para 4, 3, 2 e 1 pontos de nota. - A pontuação 42 atinge o bloco
default, que primeiro imprime(failing score 42)pela linha de efeito colateral e depois retorna 0 comyield— mostrando como um ramo de bloco pode fazer trabalho antes de produzir seu valor. - O
switchfinal atribuiTWOdiretamente à variávellabel, provando que uma expressão switch é um valor que você pode armazenar, não apenas controle de fluxo.
Quando Usar Qual Forma
- Opte por uma expressão switch (
case L ->) sempre que o objetivo for calcular e retornar um único valor. Ela é exaustiva, livre de fall-through e lê-se como uma única atribuição. - Uma instrução switch tradicional ainda faz sentido quando cada ramo é puramente um efeito colateral (registro, despacho) e não há valor a produzir.
- Use padrões de tipo quando estiver ramificando no tipo de tempo de execução de um objeto, particularmente entre as constantes de um
enumou os subtipos de um tipo selado.
Para o histórico completo e a forma com rótulo de dois pontos, consulte expressões switch Java.