Decisões com condicionais
por Tatum WinslowEntenda o conceito
Condicionais deixam o programa escolher o que fazer conforme os dados. O if executa um bloco só quando a condição entre parênteses é true; o else cobre o caso contrário; e uma cadeia if / else if / else testa as condições de cima para baixo e executa apenas o primeiro bloco verdadeiro. Em Java a condição precisa ser um boolean: if (estoque) com um int não compila.
As condições nascem dos operadores relacionais (==, !=, <, <=, >, >=) e se combinam com os lógicos: && (e), || (ou) e ! (não). && e || fazem curto-circuito: se o lado esquerdo já decide o resultado, o direito nem é avaliado, o que permite escrever texto != null && texto.equals("x") sem risco. Para comparar o conteúdo de String, use equals, nunca ==.
Para escolher entre dois valores, o operador ternário condicao ? valorSeTrue : valorSeFalse cabe numa linha. Para muitos casos sobre um mesmo valor, o switch com setas (case "a", "b" -> ...), disponível desde o Java 14, funciona como expressão: devolve um valor, não "cai" para o próximo case e usa yield quando o case precisa de um bloco.
- Numa cadeia
if/else if/else, só o primeiro bloco com condição verdadeira roda; por isso as faixas vão da mais restrita para a mais ampla. - A condição do
iftem que serboolean; uma comparação comototal >= 100já produztrueoufalse. &&e||fazem curto-circuito: emx != null && x.equals(y), oequalssó roda sexnão for nulo.- Compare o conteúdo de textos com
equals(ouequalsIgnoreCase);==compara se é o mesmo objeto. - O ternário
c ? a : bescolhe entre dois valores; oswitchcom->é uma expressão que devolve valor, sembreake sem queda entre os casos.
Do conceito à prática
3 exemplos para ver a ideia funcionando.
01Frete da loja com if, else if e else
public class Frete {
public static void main(String[] args) {
double totalCompra = 149.90;
int itens = 3;
System.out.printf("Total da compra: R$ %.2f%n", totalCompra);
// if simples: o bloco só roda se a condição for true.
if (itens > 2) {
System.out.println("Ganhou um brinde por levar mais de 2 itens!");
}
// if / else if / else: as condições são testadas de cima para baixo
// e só o primeiro bloco verdadeiro é executado.
double frete;
if (totalCompra >= 200) {
frete = 0;
} else if (totalCompra >= 100) {
frete = 9.90;
} else {
frete = 19.90;
}
System.out.printf("Frete: R$ %.2f%n", frete);
// Uma comparação produz um boolean, que pode ir para uma variável.
boolean freteGratis = frete == 0;
if (!freteGratis) {
double falta = 200 - totalCompra;
System.out.printf("Faltam R$ %.2f para o frete grátis%n", falta);
}
}
}
O primeiro if não tem else: se a compra tiver mais de 2 itens, a mensagem do brinde aparece; se não, o programa segue em frente sem fazer nada. Já a cadeia do frete testa as faixas de cima para baixo. Como 149.90 >= 200 é falso e 149.90 >= 100 é verdadeiro, o frete fica em 9.90 e o else nem é considerado.
A comparação frete == 0 produz um boolean que vai para a variável freteGratis, e !freteGratis inverte esse valor. Repare que frete foi declarado antes da cadeia e recebe valor em todos os ramos: o compilador confere isso e recusaria o uso de frete se algum caminho o deixasse sem valor.
02Regras de empréstimo com &&, || e equals
public class Emprestimo {
public static void main(String[] args) {
String tipoLeitor = "aluno";
int livrosEmprestados = 2;
boolean temMulta = false;
String livro = "Vidas Secas";
// && exige as duas condições; ! inverte um boolean.
boolean podeEmprestar = livrosEmprestados < 3 && !temMulta;
System.out.println("Pode pegar outro livro? " + podeEmprestar);
// Strings se comparam com equals, não com ==.
int prazoDias;
if (tipoLeitor.equals("professor")) {
prazoDias = 30;
} else if (tipoLeitor.equals("aluno") || tipoLeitor.equals("servidor")) {
prazoDias = 14;
} else {
prazoDias = 7;
}
System.out.println("Prazo para " + tipoLeitor + ": " + prazoDias + " dias");
// Curto-circuito: se o lado esquerdo já decide, o direito nem é avaliado.
String reserva = null;
if (reserva != null && reserva.equals(livro)) {
System.out.println("Livro reservado para você.");
} else {
System.out.println("Nenhuma reserva para " + livro + ".");
}
// equalsIgnoreCase compara ignorando maiúsculas e minúsculas.
String busca = "VIDAS SECAS";
if (busca.equalsIgnoreCase(livro)) {
System.out.println("Encontrado no acervo: " + livro);
}
}
}
A regra de empréstimo combina duas condições com &&: o leitor precisa ter menos de 3 livros e não ter multa (!temMulta). O prazo usa || para dar o mesmo valor a dois tipos de leitor, e toda comparação de texto é feita com equals, que confere o conteúdo da String.
O teste da reserva mostra o curto-circuito na prática: como reserva é null, o lado esquerdo reserva != null já é falso e o Java nem chama reserva.equals(livro), que lançaria NullPointerException. Por fim, equalsIgnoreCase encontra o livro mesmo com a busca toda em maiúsculas.
03Cardápio da semana com switch e ternário
public class Cardapio {
public static void main(String[] args) {
String dia = "sexta";
int convidados = 7;
// switch expression (Java 14+): cada case com -> devolve um valor
// e não existe "queda" para o case seguinte.
String prato = switch (dia) {
case "segunda", "quarta" -> "Sopa de legumes";
case "terça", "quinta" -> "Risoto de cogumelos";
case "sexta" -> "Moqueca de peixe";
default -> "Feijoada";
};
System.out.println("Prato de " + dia + ": " + prato);
// Um case com bloco usa yield para devolver o valor.
int tempoMinutos = switch (prato) {
case "Sopa de legumes" -> 40;
case "Moqueca de peixe" -> {
int preparo = 20;
int cozimento = 35;
yield preparo + cozimento;
}
default -> 60;
};
System.out.println("Tempo de preparo: " + tempoMinutos + " minutos");
// Operador ternário: condição ? valor se true : valor se false.
String panela = convidados > 6 ? "panela grande" : "panela média";
System.out.println("Use a " + panela + " para " + convidados + " pessoas");
// O ternário também cabe no meio de uma concatenação.
System.out.println("Sobremesa: " + (dia.equals("sexta") ? "pudim" : "frutas"));
}
}
O primeiro switch é uma expressão: o valor do case escolhido vai direto para prato. Um case pode listar vários rótulos separados por vírgula, e o default garante que sempre exista um resultado. Com a seta ->, só o case escolhido roda; não há o risco de esquecer um break e "cair" no caso seguinte, como no switch antigo com dois-pontos.
No segundo switch, o case da moqueca precisa de um bloco para calcular o tempo, então usa yield para devolver o valor. O ternário resolve escolhas entre duas opções numa linha só; dentro de uma concatenação, ele precisa de parênteses para ser avaliado antes do +.
Agora é com você
Tente resolver no rascunho antes de abrir a resposta. Os códigos não são executados nesta página.
01Situação do aluno no boletimFácil
Calcule a média de duas notas (6.5 e 5.0) e mostre a situação do aluno: aprovado com média a partir de 7, recuperação a partir de 5 e reprovado abaixo disso.
- A classe se chama
Situacaoe as notas ficam em variáveisdouble. - A média é calculada pelo programa, não digitada pronta.
- Usa uma única cadeia
if/else if/elsepara decidir a situação. - A saída é idêntica à esperada.
public class Situacao {
public static void main(String[] args) {
double nota1 = 6.5;
double nota2 = 5.0;
// Calcule a média, imprima e decida a situação.
}
}
Média: 5.75
Situação: recuperação
Ver dica
Pense na ordem dos testes: uma média 8 também é maior ou igual a 5, então qual faixa deve ser conferida primeiro?
Ver resposta
public class Situacao {
public static void main(String[] args) {
double nota1 = 6.5;
double nota2 = 5.0;
double media = (nota1 + nota2) / 2;
System.out.println("Média: " + media);
// As faixas são testadas da maior para a menor.
if (media >= 7) {
System.out.println("Situação: aprovado");
} else if (media >= 5) {
System.out.println("Situação: recuperação");
} else {
System.out.println("Situação: reprovado");
}
}
}
Passo a passo
nota1enota2sãodouble, porque notas podem ter casas decimais.(nota1 + nota2) / 2soma primeiro (graças aos parênteses) e divide depois; como os operandos sãodouble, a divisão mantém os decimais e dá5.75.if (media >= 7)testa a faixa mais alta;5.75 >= 7é falso, então o bloco é pulado.else if (media >= 5)só é avaliado porque o teste anterior falhou;5.75 >= 5é verdadeiro e a recuperação é impressa.- O
elsefinal pega todo o resto, sem precisar de condição: se nenhuma faixa anterior serviu, a média é menor que 5.
Por que funciona
Numa cadeia if / else if / else, o Java para no primeiro teste verdadeiro e ignora os demais. Por isso cada ramo pode ser escrito de forma simples: quando chegamos ao else if (media >= 5), já sabemos que a média é menor que 7, e não é preciso escrever media >= 5 && media < 7.
Testar da faixa mais alta para a mais baixa garante que cada média caia em exatamente uma situação.
Erros comuns
- Ordem invertida: começar com
if (media >= 5)faz uma média 9 sair como recuperação, porque o primeiro teste já é verdadeiro. Teste a faixa mais restrita primeiro. - Três ifs soltos:
if (media >= 7) ... if (media >= 5) ...semelseimprime duas situações para uma média 8. Encadeie comelse if. - Esquecer os parênteses:
nota1 + nota2 / 2divide só a segunda nota e dá9.0. Agrupe a soma:(nota1 + nota2) / 2. - Ponto e vírgula depois do if:
if (media >= 7);encerra oifali, e o bloco seguinte roda sempre. Não coloque;depois da condição.
Outra forma de resolver
Com um ternário aninhado, a situação cabe numa expressão:
String situacao = media >= 7 ? "aprovado" : media >= 5 ? "recuperação" : "reprovado";
System.out.println("Situação: " + situacao);
Funciona, mas com mais de duas opções fica difícil de ler; a cadeia if / else if é a forma mais clara para faixas.
Saída esperada
Média: 5.75
Situação: recuperação
02Desconto e frete no fechamento do pedidoMédio
Feche um pedido de R$ 180 de uma cliente VIP que usou o cupom FRETEGRATIS. Ela ganha 10% de desconto se a compra for de pelo menos R$ 200 ou se for VIP, e o frete de R$ 15 é zerado se o cupom for FRETEGRATIS e o valor com desconto for de pelo menos R$ 100.
- A regra do desconto usa
||e a do frete usa&&, cada uma num únicoif. - O cupom é comparado com
equals, não com==. - A linha do frete usa um ternário para mostrar
grátisou o valor. - Todos os valores são
int, e a saída é idêntica à esperada.
public class Checkout {
public static void main(String[] args) {
int total = 180;
boolean clienteVip = true;
String cupom = "FRETEGRATIS";
// Aplique o desconto, decida o frete e imprima o resumo.
}
}
Subtotal: R$ 180
Desconto: R$ 18
Frete: grátis
Total a pagar: R$ 162
Ver dica
Decida o desconto antes do frete: a regra do frete olha o valor que sobrou depois do desconto.
Ver resposta
public class Checkout {
public static void main(String[] args) {
int total = 180;
boolean clienteVip = true;
String cupom = "FRETEGRATIS";
// ||: basta uma das condições para ganhar 10% de desconto.
int desconto = 0;
if (total >= 200 || clienteVip) {
desconto = total * 10 / 100;
}
int comDesconto = total - desconto;
// &&: o cupom precisa estar certo E a compra valer pelo menos R$ 100.
int frete = 15;
if (cupom.equals("FRETEGRATIS") && comDesconto >= 100) {
frete = 0;
}
System.out.println("Subtotal: R$ " + total);
System.out.println("Desconto: R$ " + desconto);
System.out.println("Frete: " + (frete == 0 ? "grátis" : "R$ " + frete));
System.out.println("Total a pagar: R$ " + (comDesconto + frete));
}
}
Passo a passo
- As três variáveis guardam o cenário do pedido;
totaléintporque os valores são inteiros em reais. descontocomeça em0e só muda setotal >= 200 || clienteVipfor verdadeiro; comoclienteVipétrue, a cliente ganha o desconto mesmo com a compra abaixo de R$ 200.total * 10 / 100multiplica antes de dividir, então1800 / 100dá18exato, sem perder nada na divisão inteira.fretecomeça em15e só vira0quando as duas condições do&&são verdadeiras: o cupom éFRETEGRATISe162 >= 100.cupom.equals("FRETEGRATIS")compara o conteúdo do texto, letra por letra.- Na linha do frete,
frete == 0 ? "grátis" : "R$ " + freteescolhe o texto; os parênteses em volta do ternário fazem a escolha antes da concatenação com o rótulo.
Por que funciona
O || modela regras do tipo "basta um motivo" e o &&, regras do tipo "precisa de todos". Começar cada variável com o valor padrão (0 de desconto, 15 de frete) e só alterá-la dentro do if deixa o código curto: não é preciso um else que repita o valor padrão.
A ordem das regras também importa: o frete depende de comDesconto, então o desconto precisa ser calculado antes.
Erros comuns
- Comparar texto com ==:
cupom == "FRETEGRATIS"compara se as duas variáveis apontam para o mesmo objeto, não o conteúdo, e pode darfalsecom textos iguais (por exemplo, quando o cupom vem de uma digitação). Useequals. - Trocar && por ||:
cupom.equals("FRETEGRATIS") || comDesconto >= 100zera o frete de qualquer compra acima de R$ 100, mesmo sem cupom. - Dividir antes de multiplicar:
total / 100 * 10faz180 / 100, que dá1na divisão inteira, e o desconto sai10em vez de18. - Ternário sem parênteses:
"Frete: " + frete == 0 ? ...não compila, porque o+é avaliado antes do==e o Java tenta comparar umaStringcom0.
Outra forma de resolver
As condições podem virar variáveis boolean com nomes que explicam a regra:
boolean ganhaDesconto = total >= 200 || clienteVip;
int desconto = ganhaDesconto ? total * 10 / 100 : 0;
Essa forma é preferível quando a mesma condição é usada em mais de um lugar ou quando a regra tem muitas partes e um nome ajuda a lê-la.
Saída esperada
Subtotal: R$ 180
Desconto: R$ 18
Frete: grátis
Total a pagar: R$ 162
03Rolagens de ataque com switchDesafio
Num jogo de RPG, um ataque rola um dado de 20 faces: 20 é sempre acerto crítico, 1 é sempre falha crítica e qualquer outro valor acerta se o dado mais o bônus (4) alcançar a defesa (15). Classifique as rolagens 20, 11, 10 e 1 e mostre o dano de cada uma (base 8, dobrado no crítico e zero quando erra).
- Um método
static String classificar(int dado, int bonus, int defesa)devolve o resultado com umswitchde setas sobre o dado. - O
defaultdesseswitchusa um ternário para decidir entreacertoeerro. - Um segundo método,
calcularDano, usa umswitchsobre o texto do resultado. - Cada linha é impressa com
printfe a saída é idêntica à esperada.
public class Ataque {
static String classificar(int dado, int bonus, int defesa) {
// Use um switch com -> sobre o dado.
return "";
}
static int calcularDano(String resultado, int danoBase) {
// Use um switch sobre o resultado.
return 0;
}
public static void main(String[] args) {
int bonus = 4;
int defesa = 15;
int danoBase = 8;
// Classifique as rolagens 20, 11, 10 e 1 e imprima cada uma.
}
}
Dado 20: acerto crítico (16 de dano)
Dado 11: acerto (8 de dano)
Dado 10: erro (0 de dano)
Dado 1: falha crítica (0 de dano)
Ver dica
Os casos especiais (20 e 1) ganham um case próprio; a conta com o bônus só interessa para os outros valores.
Ver resposta
public class Ataque {
// Devolve o resultado de uma rolagem de d20 contra a defesa do inimigo.
static String classificar(int dado, int bonus, int defesa) {
return switch (dado) {
case 20 -> "acerto crítico";
case 1 -> "falha crítica";
default -> dado + bonus >= defesa ? "acerto" : "erro";
};
}
// O dano depende do resultado: crítico dobra, erro zera.
static int calcularDano(String resultado, int danoBase) {
return switch (resultado) {
case "acerto crítico" -> danoBase * 2;
case "acerto" -> danoBase;
default -> 0;
};
}
public static void main(String[] args) {
int bonus = 4;
int defesa = 15;
int danoBase = 8;
String r1 = classificar(20, bonus, defesa);
String r2 = classificar(11, bonus, defesa);
String r3 = classificar(10, bonus, defesa);
String r4 = classificar(1, bonus, defesa);
System.out.printf("Dado 20: %s (%d de dano)%n", r1, calcularDano(r1, danoBase));
System.out.printf("Dado 11: %s (%d de dano)%n", r2, calcularDano(r2, danoBase));
System.out.printf("Dado 10: %s (%d de dano)%n", r3, calcularDano(r3, danoBase));
System.out.printf("Dado 1: %s (%d de dano)%n", r4, calcularDano(r4, danoBase));
}
}
Passo a passo
classificarrecebe o valor do dado, o bônus e a defesa, e devolve o resultado comoString. Um métodostaticpode ser chamado direto domain, sem criar objeto, e evita repetir a mesma decisão quatro vezes.return switch (dado) { ... };devolve o valor da própria expressãoswitch; o;depois da chave de fechamento termina oreturn.case 20ecase 1tratam os valores especiais antes de qualquer conta, então um 1 com bônus alto continua sendo falha crítica.- O
defaultresolve todos os outros valores com o ternáriodado + bonus >= defesa ? "acerto" : "erro":11 + 4 = 15alcança a defesa e acerta, enquanto10 + 4 = 14erra por um ponto. calcularDanofaz umswitchsobre o texto do resultado: o crítico dobra o dano, o acerto usa o valor base e odefaultcobreerroefalha crítica, que não causam dano.- No
main, cada rolagem é classificada uma vez e o resultado fica numa variável, que é usada tanto na impressão quanto no cálculo do dano. printf("Dado 20: %s (%d de dano)%n", ...)encaixa o texto em%se o número em%d, terminando a linha com%n.
Por que funciona
O switch com setas é uma expressão que precisa sempre produzir um valor, por isso o compilador exige o default quando os casos não cobrem todos os inteiros ou todos os textos possíveis. Os casos fixos (20 e 1) ficam em case próprios, e a regra geral, que depende de uma comparação, fica no default com um ternário: cada ferramenta faz o que sabe fazer melhor.
Separar a classificação do cálculo de dano deixa cada decisão num lugar só. Se a regra do crítico mudar (triplicar o dano, por exemplo), basta alterar uma linha de calcularDano.
Erros comuns
- Testar a soma antes dos casos especiais:
if (dado + bonus >= defesa) ... else if (dado == 20)nunca chega ao crítico, porque 20 + 4 já passa pelo primeiro teste e sai como acerto simples. Trate o 20 e o 1 primeiro. - Esquecer o default: um
switchexpressão sobreintsó comcase 20ecase 1não compila ("the switch expression does not cover all possible input values"). - Misturar seta e dois-pontos:
case 20: "acerto crítico";dentro de umswitchcom->não compila. Use só setas, eyieldse o caso precisar de um bloco. - Comparar o resultado com ==:
if (resultado == "acerto")depende de os textos serem o mesmo objeto. NoswitchsobreString, o Java já compara comequals; fora dele, useequalstambém. - Esquecer o ; depois do switch:
return switch (dado) { ... }sem;gera "';' expected", porque oswitchaqui é parte de uma instruçãoreturn.
Outra forma de resolver
A mesma classificação pode ser escrita com uma cadeia if / else if:
static String classificar(int dado, int bonus, int defesa) {
if (dado == 20) {
return "acerto crítico";
} else if (dado == 1) {
return "falha crítica";
} else if (dado + bonus >= defesa) {
return "acerto";
}
return "erro";
}
O if é preferível quando cada ramo depende de uma condição diferente; o switch fica mais limpo quando as decisões giram em torno de valores fixos de uma mesma variável, como os lados de um dado.
Saída esperada
Dado 20: acerto crítico (16 de dano)
Dado 11: acerto (8 de dano)
Dado 10: erro (0 de dano)
Dado 1: falha crítica (0 de dano)