Java switch com Setas Java 14+
Na aula switch você viu o switch clássico, com case, : e break, e o perigo do fall-through. Desde o Java 14, existe uma forma nova: case valor -> …, com uma seta. Ela não tem fall-through, não precisa de break e, o mais importante, pode ser usada como uma expressão, que devolve um valor.
- A sintaxe com seta
case 1 ->e por que ela dispensa obreak. - Vários rótulos num caso:
case 1, 7 ->. - O
switchcomo expressão:String nome = switch (dia) { … };. - Blocos com várias linhas e o
yield, que devolve o valor do bloco. - Exaustividade: por que o
javacexige que todos os valores sejam tratados (e o erro real quando não são). - A comparação entre o clássico e o moderno, e uma prévia dos padrões no
switch.
A ideia: cada caso é uma regra completa
No switch clássico, os case eram pontos de entrada numa escada: você entra num degrau e desce até um break. No switch com setas, cada case é uma regra independente: "se for 1, faça isto" e acabou. Pense na diferença entre um corredor com portas (você entra por uma e pode atravessar as outras) e um painel de interruptores, em que cada um liga só a sua lâmpada.
A seta ->: sem break, sem fall-through
public class Main {
public static void main(String[] args) {
int dia = 3;
switch (dia) {
case 1 -> System.out.println("Domingo");
case 2 -> System.out.println("Segunda");
case 3 -> System.out.println("Terça");
case 4 -> System.out.println("Quarta");
case 5 -> System.out.println("Quinta");
case 6 -> System.out.println("Sexta");
case 7 -> System.out.println("Sábado");
default -> System.out.println("Dia inválido");
}
System.out.println("Fim");
}
}
$ java Main.java Terça « só o caso 3: nenhum break e nenhum fall-through Fim
- case 3
- O valor do caso, como antes.
- ->
- A seta: um hífen seguido de "maior que", sem espaço entre eles. Lê-se "então faça".
- System.out.println("Terça");
- O que fazer nesse caso: uma instrução, um bloco
{ … }ou (como você vai ver) um valor. Depois dela, oswitchtermina. Não há como "cair" no caso de baixo. - default ->
- O "senão", também com seta.
Compare com o programa da aula anterior: 8 linhas de break a menos e nenhuma chance de esquecer uma delas.
Vários rótulos e blocos
Para vários valores com a mesma regra, separe-os por vírgula: case 1, 7 ->. Para várias linhas, use chaves; cada bloco tem as suas próprias variáveis (no clássico, todos os casos dividiam o mesmo bloco):
public class Main {
public static void main(String[] args) {
int dia = 2;
switch (dia) {
case 1 -> {
System.out.println("Domingo");
System.out.println("Dia de descanso");
}
case 2 -> {
String msg = "Segunda";
System.out.println(msg);
}
case 3 -> {
String msg = "Terça";
System.out.println(msg);
}
default -> {}
}
}
}
$ java Main.java Segunda
As duas variáveis msg convivem sem erro, porque cada uma mora no seu bloco (no switch clássico isso dava variable msg is already defined). default -> {} é um bloco vazio: "nos outros casos, não faça nada".
O switch como expressão
Aqui está a grande novidade. Muitas vezes o switch existe só para escolher um valor: o nome do dia, o preço do produto, a quantidade de dias do mês. Em vez de atribuir a variável dentro de cada caso, o switch inteiro pode ser o valor, como um ternário com muitas opções:
public class Main {
public static void main(String[] args) {
int dia = 7;
String nome = switch (dia) {
case 1 -> "Domingo";
case 2 -> "Segunda";
case 3 -> "Terça";
case 4 -> "Quarta";
case 5 -> "Quinta";
case 6 -> "Sexta";
case 7 -> "Sábado";
default -> "Dia inválido";
};
System.out.println("Hoje é " + nome);
String tipo = switch (dia) {
case 1, 7 -> "fim de semana";
case 2, 3, 4, 5, 6 -> "dia útil";
default -> "?";
};
System.out.println("É " + tipo);
System.out.println(switch (dia) { case 1, 7 -> "descanso"; default -> "trabalho"; });
}
}
$ java Main.java Hoje é Sábado É fim de semana descanso
- String nome = switch (dia) {
- O
switchaparece do lado direito do=: o valor que ele produzir vai paranome. - case 1 -> "Domingo";
- Depois da seta vem o valor daquele caso, seguido de
;. - default -> "Dia inválido";
- O valor para todos os outros casos. Num
switchde expressão, alguma regra tem de servir para cada valor possível (veja "Exaustividade"). - };
- O
;depois da chave é obrigatório: a linha inteiraString nome = switch … ;é uma instrução de atribuição. - System.out.println(switch …)
- Como é um valor, cabe em qualquer lugar que aceite um valor, até dentro do
println(mas não exagere na legibilidade).
Compare com o equivalente clássico, que precisava declarar String nome; antes, atribuir em cada case e não esquecer nenhum break. Aqui, a variável nasce pronta, e o javac garante que ela sempre recebe um valor.
Missão: transforme o switch clássico num switch de expressão, com setas: String bebida = switch (opcao) { … };. As opções: 1 = Café, 2 = Chá, 3 = Suco, outras = Água. A saída deve continuar Bebida: Chá.
public class Main {
public static void main(String[] args) {
int opcao = 2;
String bebida;
switch (opcao) {
case 1:
bebida = "Café";
break;
case 2:
bebida = "Chá";
break;
case 3:
bebida = "Suco";
break;
default:
bebida = "Água";
}
System.out.println("Bebida: " + bebida);
}
}
public class Main {
public static void main(String[] args) {
int opcao = 2;
String bebida = switch (opcao) {
case 1 -> "Café";
case 2 -> "Chá";
case 3 -> "Suco";
default -> "Água";
};
System.out.println("Bebida: " + bebida);
}
}
Blocos que devolvem valor: yield
E se, para calcular o valor de um caso, você precisar de várias linhas? Use um bloco { … } e, no fim, yield ("render", "produzir") com o valor que o bloco devolve:
public class Main {
public static void main(String[] args) {
int mes = 2;
int ano = 2028;
int dias = switch (mes) {
case 4, 6, 9, 11 -> 30;
case 2 -> {
boolean bissexto = (ano % 4 == 0 && ano % 100 != 0) || ano % 400 == 0;
System.out.println("Fevereiro de " + ano + (bissexto ? " é bissexto" : " não é bissexto"));
yield bissexto ? 29 : 28;
}
default -> 31;
};
System.out.println("Dias: " + dias);
}
}
$ java Main.java Fevereiro de 2028 é bissexto Dias: 29
- case 2 -> { … }
- Um bloco com várias instruções: calcula se o ano é bissexto (a regra do calendário: divisível por 4, exceto os séculos que não são divisíveis por 400) e mostra uma mensagem.
- yield bissexto ? 29 : 28;
- "Este bloco produz o valor 29 ou 28". O
yieldencerra o bloco e entrega o valor aoswitch, que o entrega adias.
yield × return
O yield devolve um valor do switch. O return (que você vai ver em Valores de Retorno) sai do método inteiro, e é proibido dentro de um switch de expressão (veja "Quando dá errado"). Nos casos de uma linha (case 4 -> 30;) o yield fica implícito; ele só aparece dentro de blocos.
yield com a sintaxe antiga
O switch de expressão também aceita os dois-pontos do estilo clássico; nesse caso, todo caso usa yield, e o fall-through volta a existir (um caso sem yield cai no de baixo). É raro ver isso; prefira as setas.
public class Main {
public static void main(String[] args) {
char conceito = 'B';
String texto = switch (conceito) {
case 'A':
yield "Excelente";
case 'B':
yield "Bom";
case 'C':
yield "Regular";
default:
yield "Insuficiente";
};
System.out.println(texto);
}
}
$ java Main.java Bom
Exaustividade: todos os casos cobertos
Um switch de expressão precisa produzir um valor sempre, para qualquer valor do seletor. Se existir algum valor sem regra, o que a variável receberia? Por isso o javac exige que o switch seja exaustivo (cubra todas as possibilidades). Este código tem um erro de propósito.
public class Main {
public static void main(String[] args) {
int dia = 3;
String nome = switch (dia) {
case 1 -> "Domingo";
case 2 -> "Segunda";
case 3 -> "Terça";
};
System.out.println(nome);
}
}
$ javac Main.java
Main.java:4: error: the switch expression does not cover all possible input values
String nome = switch (dia) {
^
1 error
"A expressão switch não cobre todos os valores de entrada possíveis." Não adianta que dia seja 3 agora: o javac olha o tipo, e um int pode valer qualquer coisa entre -2 bilhões e +2 bilhões. Com um default, todos ficam cobertos.
Com enum, o default é dispensável
Um enum tem uma lista fechada de valores. Se você trata todos, o switch já é exaustivo, sem default:
public class Main {
enum Semaforo { VERDE, AMARELO, VERMELHO }
public static void main(String[] args) {
Semaforo sinal = Semaforo.VERMELHO;
String acao = switch (sinal) {
case VERDE -> "Pode seguir";
case AMARELO -> "Atenção";
case VERMELHO -> "Pare!";
};
System.out.println(acao);
}
}
$ java Main.java Pare!
E aqui está a grande vantagem: se você esquecer um valor (ou se alguém, um dia, acrescentar PISCANDO ao enum), o javac avisa na hora. Com case VERMELHO removido:
$ javac Main.java
Main.java:6: error: the switch expression does not cover all possible input values
String acao = switch (sinal) {
^
1 error
Por isso, com enums, é melhor não colocar default: ele "cobriria" o valor novo em silêncio, e o javac não teria como avisar.
switch com setas que não devolve valor?
Usado como instrução (como no primeiro exemplo, só com println), o switch com setas sobre int, String ou enum não precisa ser exaustivo: se nenhum caso bater, nada acontece, como no clássico. A exigência vale para o switch de expressão (e para o switch com padrões).
Clássico × com setas
Clássico (case x:) | Com setas (case x ->) | |
|---|---|---|
| Desde | Java 1.0 | Java 14 |
break | Necessário em cada caso | Não existe |
| Fall-through | Sim (sem break, cai no próximo) | Não |
| Vários valores | case 1: case 7: ou case 1, 7: | case 1, 7 -> |
| Variáveis nos casos | Todos dividem um bloco | Cada bloco tem as suas |
| Como expressão (devolve valor) | Só com yield em cada caso | Sim: case 1 -> "valor"; |
| Exaustividade verificada | Não (como instrução) | Sim, quando é expressão |
| Misturar os dois estilos | Proibido no mesmo switch (erro different case kinds used in the switch) | |
Em código novo, prefira as setas: são mais curtas, mais seguras e deixam claro, caso a caso, o que acontece. O clássico continua importante para ler código antigo e para os raros casos em que o fall-through é desejado.
Prévia: padrões no switch Java 21+
O switch moderno faz ainda mais: ele pode testar o tipo de um valor, dar nome a ele e acrescentar condições com when. Só para você reconhecer quando encontrar:
public class Main {
public static void main(String[] args) {
Object[] coisas = {42, "Ana", 3.5, null};
for (Object o : coisas) {
String descricao = switch (o) {
case null -> "nada";
case Integer i when i > 40 -> "inteiro grande: " + i;
case Integer i -> "inteiro: " + i;
case String s -> "texto com " + s.length() + " letras";
default -> "outra coisa: " + o;
};
System.out.println(descricao);
}
}
}
$ java Main.java inteiro grande: 42 texto com 3 letras outra coisa: 3.5 nada
case Integer i quer dizer "se for um inteiro, chame-o de i"; when i > 40 acrescenta uma condição; case null trata o null sem NullPointerException. Isso depende de conceitos do Nível 2 e 3 (objetos, Object, for-each), e é explicado em detalhe em Pattern Matching.
Quando dá errado
error: the switch expression does not cover all possible input values
O que aconteceu: o switch de expressão não tem regra para algum valor possível do seletor (veja "Exaustividade").
Como resolver: com int ou String, acrescente default -> …;. Com enum, acrescente o caso que falta.
error: different case kinds used in the switch
Este código tem um erro de propósito.
public class Main {
public static void main(String[] args) {
int dia = 1;
switch (dia) {
case 1 -> System.out.println("Domingo");
case 2:
System.out.println("Segunda");
break;
}
}
}
$ javac Main.java
Main.java:6: error: different case kinds used in the switch
case 2:
^
1 error
O que aconteceu: "tipos de case diferentes usados no switch": um caso com seta e outro com dois-pontos. Cada switch tem de usar um estilo só.
Como resolver: passe todos para setas (e tire os break).
error: ';' expected depois do }
Este código tem um erro de propósito.
public class Main {
public static void main(String[] args) {
int dia = 1;
String nome = switch (dia) {
case 1 -> "Domingo";
default -> "Outro";
}
System.out.println(nome);
}
}
$ javac Main.java
Main.java:7: error: ';' expected
}
^
1 error
O que aconteceu: um switch de expressão é parte de uma atribuição, e toda atribuição termina com ;.
Como resolver: };.
error: switch rule completes without providing a value
Este código tem um erro de propósito.
public class Main {
public static void main(String[] args) {
int mes = 2;
int dias = switch (mes) {
case 2 -> {
System.out.println("Fevereiro");
}
default -> 31;
};
System.out.println(dias);
}
}
$ javac Main.java
Main.java:7: error: switch rule completes without providing a value
}
^
(switch rules in switch expressions must either provide a value or throw)
1 error
O que aconteceu: "a regra do switch termina sem fornecer um valor". O bloco do case 2 chegou ao } sem yield. A linha entre parênteses explica: num switch de expressão, cada regra tem de fornecer um valor ou lançar uma exceção (throw, veja throw e throws).
Como resolver: termine o bloco com yield 28; (ou o valor certo).
error: attempt to return out of a switch expression
Este código tem um erro de propósito.
public class Main {
public static void main(String[] args) {
String cmd = "sair";
int codigo = switch (cmd) {
case "novo" -> 1;
case "sair" -> {
return;
}
default -> 0;
};
System.out.println(codigo);
}
}
$ javac Main.java
Main.java:7: error: attempt to return out of a switch expression
return;
^
1 error
O que aconteceu: "tentativa de retornar de dentro de uma expressão switch". O return sairia do método no meio de uma atribuição, e codigo ficaria sem valor.
Como resolver: use yield para devolver um valor do switch; se precisa sair do método, decida isso depois, com um if sobre o valor.
Referência
| Forma | Significado |
|---|---|
case 1 -> instrução; | Regra com uma instrução (sem break) |
case 1 -> { … } | Regra com um bloco |
case 1, 7 -> | Vários valores na mesma regra |
default -> | Todos os outros valores |
tipo x = switch (s) { case a -> v1; default -> v2; }; | switch de expressão (note o ; final) |
case a -> { …; yield v; } | Bloco que devolve v |
case a: yield v; | yield no estilo de dois-pontos |
case Integer i when i > 0 -> | Padrão com condição (Java 21+, veja Pattern Matching) |
case null -> | Trata null (Java 21+) |
Exercícios
Missão final: numa pizzaria, o tamanho "P" custa 12.0, o "M" custa 17.5 e o "G" custa 22.5; outros tamanhos, 0.0. Use um switch de expressão com setas. No caso "G", use um bloco que mostre Tamanho grande ganha brinde! e devolva o preço com yield. Com tamanho = "G", a saída deve ser:Tamanho grande ganha brinde!Preço: 22.5
public class Main {
public static void main(String[] args) {
String tamanho = "G";
// double preco = switch (tamanho) { … };
}
}
public class Main {
public static void main(String[] args) {
String tamanho = "G";
double preco = switch (tamanho) {
case "P" -> 12.0;
case "M" -> 17.5;
case "G" -> {
System.out.println("Tamanho grande ganha brinde!");
yield 22.5;
}
default -> 0.0;
};
System.out.println("Preço: " + preco);
}
}
1. A seta
Complete o caso para devolver "um" quando o valor for 1:
2. Devolver de um bloco
Qual palavra devolve o valor de um bloco dentro de um switch de expressão?
3. Vários valores
Complete para que 6 e 7 tenham a mesma regra:
4. Com int x = 2;, o que este código mostra?
switch (x) {
case 1 -> System.out.println("um");
case 2 -> System.out.println("dois");
case 3 -> System.out.println("três");
}
5. Por que String s = switch (n) { case 1 -> "a"; case 2 -> "b"; }; não compila (com int n)?
6. Num switch de expressão sobre um enum que trata todos os valores, é melhor…
Resumo
case x ->(Java 14+): cada caso é uma regra completa. Sembreake sem fall-through.- Vários valores:
case 1, 7 ->. Várias linhas:case 1 -> { … }, cada bloco com as suas variáveis. - Como expressão:
tipo x = switch (s) { case a -> valor; … };(com;no fim). - Em blocos de um
switchde expressão,yield valor;devolve o valor;returné proibido. - O
switchde expressão tem de ser exaustivo:defaultparaint/String; todos os valores paraenum(e aí, melhor semdefault). - Não misture
:e->no mesmoswitch. Padrões (case Integer i when …,case null) são do Java 21+: veja Pattern Matching.