Java switch
Quando você precisa comparar uma variável com uma lista de valores exatos (o dia 1 é domingo, o 2 é segunda…; o comando "novo", "ajuda" ou "sair"), uma escada de else if fica comprida e repetitiva. O switch ("chave", no sentido de "comutador") faz isso de forma organizada: você diz qual variável examinar e lista os casos.
- A estrutura do
switchclássico:case,breakedefault. - O que é o fall-through (o "cair para o próximo caso") e o que acontece de verdade quando você esquece o
break. - Como juntar vários
caseque fazem a mesma coisa. switchcomString,chareenum.- Os erros reais:
duplicate case label,constant expression required,NullPointerExceptione oswitchcomdouble.
Esta aula trata do switch "clássico", que existe desde a primeira versão do Java e que você vai encontrar em todo código antigo. A próxima aula, switch com Setas, mostra a forma moderna (Java 14+), mais curta e mais segura. Vale aprender as duas.
A ideia: um painel de botões
Pense no painel de um elevador: você aperta o número do andar, e o elevador vai direto para ele, sem parar em cada andar perguntando "é este?". O switch funciona assim: olha o valor da variável e salta direto para o case correspondente. Se não houver botão para aquele valor, vai para o default ("padrão").
O switch clássico
public class Main {
public static void main(String[] args) {
int dia = 3;
switch (dia) {
case 1:
System.out.println("Domingo");
break;
case 2:
System.out.println("Segunda");
break;
case 3:
System.out.println("Terça");
break;
case 4:
System.out.println("Quarta");
break;
case 5:
System.out.println("Quinta");
break;
case 6:
System.out.println("Sexta");
break;
case 7:
System.out.println("Sábado");
break;
default:
System.out.println("Dia inválido");
}
System.out.println("Fim");
}
}
$ java Main.java Terça Fim
- switch (dia)
- A variável que será examinada (o "seletor"), entre parênteses. Pode ser
int,char,short,byte,Stringouenum. - { … }
- As chaves envolvem todos os casos.
- case 3:
- "Caso o valor seja 3, comece aqui". Depois do
casevem um valor fixo (um literal ou uma constante) e dois-pontos. Oscasefuncionam como rótulos, etiquetas que marcam pontos de entrada. - break;
- "Quebre", "saia": termina o
switche pula para depois do}. Sem ele, acontece algo surpreendente (a próxima seção). - default:
- O "senão": roda quando nenhum
casebateu. É opcional; sem ele, se nenhum caso bater, nada acontece. Por costume, fica no fim e não precisa debreak.
Com dia = 3, o Java saltou direto para case 3:, mostrou Terça, encontrou o break e saiu. Com dia = 9, iria para o default e mostraria Dia inválido.
O mesmo programa com else if teria sete comparações dia == 1, dia == 2… O switch deixa claro, já na primeira linha, que tudo gira em torno de uma variável.
O fall-through: esqueci o break
Aqui está a característica mais famosa (e mais perigosa) do switch clássico. Os case são só pontos de entrada: o Java entra no case certo e, a partir dali, executa tudo para baixo, atravessando os outros case, até encontrar um break ou o fim do switch. Isso se chama fall-through ("cair através"). Veja o mesmo programa sem os break:
Este código tem um erro de propósito (de lógica: ele compila).
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 « entrou no case 3, como deveria… Quarta « …e continuou descendo por todos os outros Quinta Sexta Sábado Dia inválido « até o default! Fim
O javac não reclama de nada: o código é válido. Pense no elevador sem freio: você apertou o 3, ele chegou ao 3… e continuou descendo até o térreo. O break é o freio. Repare que os casos acima do 3 (Domingo, Segunda) não rodaram: o Java salta para o ponto de entrada e só desce a partir dali.
case termina com break
No switch clássico, a regra é: cada caso termina com break;, a menos que você queira de propósito que ele continue no próximo (e, nesse caso, deixe um comentário dizendo isso). Esquecer o break é tão comum que o javac tem um aviso para isso: compile com javac -Xlint:fallthrough. O switch com setas acabou com esse problema.
Com o programa acima (sem os break), o aviso fica assim:
$ javac -Xlint:fallthrough Main.java
Main.java:7: warning: [fallthrough] possible fall-through into case
case 2:
^
Main.java:9: warning: [fallthrough] possible fall-through into case
case 3:
^
Main.java:11: warning: [fallthrough] possible fall-through into case
case 4:
^
Main.java:13: warning: [fallthrough] possible fall-through into case
case 5:
^
Main.java:15: warning: [fallthrough] possible fall-through into case
case 6:
^
Main.java:17: warning: [fallthrough] possible fall-through into case
case 7:
^
Main.java:19: warning: [fallthrough] possible fall-through into case
default:
^
7 warnings
Possible fall-through into case = "possível queda para dentro do case": um aviso para cada case em que se pode "cair" vindo do caso de cima. São avisos (warnings), não erros: o .class é gerado do mesmo jeito.
Missão: rode e veja o fall-through mostrar três linhas em vez de uma. Coloque os break que faltam para que a saída seja só Terça e depois Fim.
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");
default:
System.out.println("Outro dia");
}
System.out.println("Fim");
}
}
public class Main {
public static void main(String[] args) {
int dia = 3;
switch (dia) {
case 1:
System.out.println("Domingo");
break;
case 2:
System.out.println("Segunda");
break;
case 3:
System.out.println("Terça");
break;
case 4:
System.out.println("Quarta");
break;
default:
System.out.println("Outro dia");
}
System.out.println("Fim");
}
}
Fall-through de propósito
Às vezes o fall-through é exatamente o que você quer: cada nível ganha os bônus dele e os de todos os níveis abaixo. Repare na ordem dos casos, do maior para o menor:
public class Main {
public static void main(String[] args) {
int nivel = 2;
System.out.println("Bônus do nível " + nivel + ":");
switch (nivel) {
case 3:
System.out.println("+ escudo");
case 2:
System.out.println("+ espada");
case 1:
System.out.println("+ poção");
}
}
}
$ java Main.java Bônus do nível 2: + espada « entrou no 2… + poção « …e caiu no 1, de propósito
Funciona, mas quem lê o código não sabe se a falta do break foi intencional. Se usar esse truque, deixe um comentário como // continua no próximo caso.
Vários case com o mesmo resultado
O uso mais comum e legítimo do fall-through é empilhar case vazios: todos caem no mesmo bloco. Desde o Java 14, há também uma forma mais curta: vários valores separados por vírgula num case só.
public class Main {
public static void main(String[] args) {
int mes = 4;
int dias;
switch (mes) {
case 2:
dias = 28;
break;
case 4:
case 6:
case 9:
case 11:
dias = 30;
break;
default:
dias = 31;
}
System.out.println("O mês " + mes + " tem " + dias + " dias");
int dia = 7;
switch (dia) {
case 1, 7:
System.out.println("Fim de semana");
break;
case 2, 3, 4, 5, 6:
System.out.println("Dia útil");
break;
}
}
}
$ java Main.java O mês 4 tem 30 dias Fim de semana
- case 4: case 6: case 9: case 11:
- Quatro pontos de entrada seguidos, sem nada entre eles. Entrando por qualquer um, o Java desce até
dias = 30;e para nobreak. - case 1, 7:
- Java 14+ A mesma ideia numa linha: "caso seja 1 ou 7".
- default: dias = 31;
- Todos os outros meses. Graças ao
default, ojavacsabe quediassempre recebe um valor. (Fevereiro com 28 dias é uma simplificação: anos bissextos são assunto de Datas e Horas.)
switch com String
Desde o Java 7, o seletor pode ser uma String. Os case são comparados com equals (o conteúdo), então aqui não existe a pegadinha do ==. A comparação diferencia maiúsculas de minúsculas:
public class Main {
public static void main(String[] args) {
String comando = "ajuda";
switch (comando) {
case "novo":
System.out.println("Criando um novo arquivo...");
break;
case "ajuda":
System.out.println("Comandos: novo, ajuda, sair");
break;
case "sair":
System.out.println("Até logo!");
break;
default:
System.out.println("Comando desconhecido: " + comando);
}
String outro = "AJUDA";
switch (outro) {
case "ajuda":
System.out.println("achou");
break;
default:
System.out.println("Comando desconhecido: " + outro);
}
switch (outro.toLowerCase()) {
case "ajuda":
System.out.println("achou depois do toLowerCase");
break;
}
}
}
$ java Main.java Comandos: novo, ajuda, sair Comando desconhecido: AJUDA « "AJUDA" não é "ajuda" achou depois do toLowerCase « padronizar antes resolve
Quando o texto vem de uma pessoa, padronize no próprio seletor: switch (comando.strip().toLowerCase()) (veja Métodos de String). E cuidado com null: um switch clássico com String null lança NullPointerException (veja "Quando dá errado").
switch com char
public class Main {
public static void main(String[] args) {
char conceito = 'B';
switch (conceito) {
case 'A':
System.out.println("Excelente");
break;
case 'B':
System.out.println("Bom");
break;
case 'C':
System.out.println("Regular");
break;
default:
System.out.println("Insuficiente");
}
}
}
$ java Main.java Bom
Com char, os valores dos case vão entre aspas simples ('A'), como todo char (veja char e boolean).
switch com enum
Um enum (de enumeração) é um tipo com uma lista fechada de valores possíveis, como as cores de um semáforo. Ele tem aula própria em Enums; aqui, basta ver que ele combina perfeitamente com o switch:
public class Main {
enum Semaforo { VERDE, AMARELO, VERMELHO }
public static void main(String[] args) {
Semaforo sinal = Semaforo.AMARELO;
switch (sinal) {
case VERDE:
System.out.println("Pode seguir");
break;
case AMARELO:
System.out.println("Atenção, vai fechar");
break;
case VERMELHO:
System.out.println("Pare!");
break;
}
}
}
$ java Main.java Atenção, vai fechar
- enum Semaforo { VERDE, AMARELO, VERMELHO }
- Cria o tipo
Semaforo, que só pode valer uma dessas três coisas. - Semaforo sinal = Semaforo.AMARELO;
- Fora do
switch, o valor é escrito com o nome do tipo na frente. - case AMARELO:
- Dentro do
case, só o nome (AMARELO), semSemaforo.na frente: o Java já sabe de que tipo se trata.
Com enums, o switch moderno ainda verifica se você tratou todos os valores; veja switch com Setas.
switch ou if/else if?
| Situação | Use |
|---|---|
| Uma variável comparada com vários valores exatos | switch |
Faixas de valores (nota >= 7, idade < 18) | if/else if (o case clássico não aceita comparações) |
| Condições sobre variáveis diferentes | if/else if |
Valores double, long ou boolean | if/else if |
| Escolher um valor conforme o caso | switch com setas (como expressão) |
| Testar o tipo de um objeto | Pattern Matching no switch |
Quando dá errado
O que aconteceu: fall-through: faltou o break no fim de um ou mais case. O Java entrou no caso certo e continuou descendo.
Como resolver: coloque break; no fim de cada caso. Para o javac avisar, compile com -Xlint:fallthrough. Ou passe para o switch com setas, que não tem fall-through.
error: duplicate case label
Este código tem um erro de propósito.
public class Main {
public static void main(String[] args) {
int opcao = 1;
switch (opcao) {
case 1:
System.out.println("Um");
break;
case 1:
System.out.println("Um de novo");
break;
}
}
}
$ javac Main.java
Main.java:8: error: duplicate case label
case 1:
^
1 error
O que aconteceu: "rótulo de case duplicado": dois case com o mesmo valor. O Java não saberia para qual saltar. Costuma acontecer ao copiar e colar um caso e esquecer de trocar o valor.
Como resolver: troque o valor ou junte os dois blocos num só.
error: constant expression required
Este código tem um erro de propósito.
public class Main {
public static void main(String[] args) {
int limite = 10;
int valor = 10;
switch (valor) {
case limite:
System.out.println("No limite");
break;
}
}
}
$ javac Main.java
Main.java:6: error: constant expression required
case limite:
^
1 error
O que aconteceu: "exige-se uma expressão constante". Os valores dos case precisam ser conhecidos na hora de compilar, e uma variável comum pode mudar.
Como resolver: use um literal (case 10:) ou uma constante: final int LIMITE = 10; (veja Constantes). Se o valor realmente varia, use if.
error: incompatible types: boolean cannot be converted to int (comparação no case)
Este código tem um erro de propósito.
public class Main {
public static void main(String[] args) {
int idade = 20;
switch (idade) {
case idade >= 18:
System.out.println("Adulto");
break;
}
}
}
$ javac Main.java
Main.java:5: error: incompatible types: boolean cannot be converted to int
case idade >= 18:
^
1 error
O que aconteceu: o case compara o seletor (um int) com um valor; idade >= 18 é um boolean.
Como resolver: faixas de valores pedem if/else if. (No Java 21+, o switch com padrões aceita condições com when; veja Pattern Matching.)
error: primitive patterns are a preview feature (switch com double ou boolean)
Este código tem um erro de propósito.
public class Main {
public static void main(String[] args) {
double nota = 7.0;
switch (nota) {
case 7.0:
System.out.println("Sete");
break;
}
}
}
$ javac Main.java
Main.java:4: error: primitive patterns are a preview feature and are disabled by default.
switch (nota) {
^
(use --enable-preview to enable primitive patterns)
1 error
O que aconteceu: o switch aceita int, char, short, byte, String e enum (e objetos, com padrões). double, float, long e boolean ficam de fora. A mensagem do JDK 25 conta uma novidade: um recurso em teste (preview feature, "recurso em pré-estreia") permitirá esses tipos no futuro, mas ele vem desligado e não deve ser usado em código normal.
Como resolver: use if/else if. Comparar double com == já é arriscado (veja double e float).
NullPointerException num switch com String
Este código tem um erro de propósito.
public class Main {
public static void main(String[] args) {
String comando = null;
switch (comando) {
case "sair":
System.out.println("Até logo!");
break;
default:
System.out.println("Outro comando");
}
}
}
$ java Main.java Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.hashCode()" because "<local2>" is null at Main.main(Main.java:4)
O que aconteceu: o switch clássico não aceita null, nem mesmo no default. A mensagem fala de hashCode() porque, por dentro, o switch com String calcula um "código" do texto para saltar mais rápido, e não dá para calcular o código de null. O <local2> é uma variável auxiliar que o javac criou para isso.
Como resolver: teste antes: if (comando != null) { switch … }. No Java 21+, o switch pode ter um case null (veja Pattern Matching).
error: variable msg is already defined in method main(String[])
Este código tem um erro de propósito.
public class Main {
public static void main(String[] args) {
int opcao = 2;
switch (opcao) {
case 1:
String msg = "um";
System.out.println(msg);
break;
case 2:
String msg = "dois";
System.out.println(msg);
break;
}
}
}
$ javac Main.java
Main.java:10: error: variable msg is already defined in method main(String[])
String msg = "dois";
^
1 error
O que aconteceu: no switch clássico, todos os case dividem um único bloco (as chaves do switch). Para o Java, as duas msg estão no mesmo lugar: "a variável msg já foi definida".
Como resolver: declare a variável uma vez, antes do switch, ou coloque cada caso entre chaves próprias: case 1: { String msg = "um"; … break; }. No switch com setas, cada caso já tem o seu bloco.
Referência
| Parte | O que faz |
|---|---|
switch (x) { … } | Examina o valor de x (int, char, short, byte, String, enum) |
case valor: | Ponto de entrada quando x é igual a valor (constante) |
case a, b, c: | Vários valores no mesmo caso (Java 14+) |
case a: case b: | Casos empilhados (fall-through intencional) |
break; | Sai do switch |
default: | Quando nenhum case bate (opcional) |
Sem break | Continua executando os casos de baixo (fall-through) |
Exercícios
Missão final: na feira da Ana, maçã custa R$ 3; banana e pera custam R$ 2 (use dois case juntos); qualquer outra fruta mostra Fruta em falta. Escreva o switch. Com fruta = "banana", a saída deve ser Preço: R$ 2.
public class Main {
public static void main(String[] args) {
String fruta = "banana";
// escreva o switch
}
}
public class Main {
public static void main(String[] args) {
String fruta = "banana";
switch (fruta) {
case "maçã":
System.out.println("Preço: R$ 3");
break;
case "banana":
case "pera":
System.out.println("Preço: R$ 2");
break;
default:
System.out.println("Fruta em falta");
}
}
}
1. Sair do switch
Qual palavra encerra um case e sai do switch?
2. O caso que sobra
Qual palavra marca o bloco que roda quando nenhum case bate?
3. 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");
}
4. Qual destes não pode ser o seletor de um switch clássico?
5. Para classificar uma nota em faixas (≥ 9, ≥ 7, ≥ 5), o melhor é…
Resumo
switch (x)salta direto para ocasecom o valor dex;defaultpega o resto.- Os
casesão pontos de entrada: sembreak, a execução continua nos casos de baixo (fall-through). Termine cada caso combreak;. - Casos empilhados (
case 4: case 6:) ou com vírgula (case 4, 6:, Java 14+) compartilham o mesmo bloco. - Funciona com
int,char,short,byte,String(comparada comequals) eenum; não comdouble,long,boolean. - Os valores dos
casesão constantes; faixas e condições pedemif. - String
nullno seletor dáNullPointerException.