Java if, else if, else

Até agora, os seus programas rodavam todas as linhas, de cima para baixo, sempre. Com o if, o programa passa a escolher: roda um trecho só quando uma condição é verdadeira, e outro trecho quando não é. É aqui que o programa começa a "pensar".

Você vai aprender
  • Como o if ("se") roda um bloco só quando a condição é true.
  • Como o else ("senão") dá um caminho alternativo.
  • Como o else if escolhe entre várias opções, e por que a ordem importa.
  • O que são blocos e chaves, e o perigo real de omitir as chaves (e de um ; a mais).
  • Como colocar um if dentro de outro e comparar Strings no if.
  • Os erros reais: 'else' without 'if', = no lugar de ==, variável que "sumiu" depois do bloco.

A ideia: uma bifurcação no caminho

Pense num GPS: "se a rua estiver bloqueada, vire à direita; senão, siga em frente". O programa é o carro, andando linha por linha. O if é a bifurcação: o caminho escolhido depende da resposta a uma pergunta de sim ou não, uma expressão booleana.


if: fazer algo só se…

public class Main {
    public static void main(String[] args) {
        double nota = 8.5;
        if (nota >= 7.0) {
            System.out.println("Aprovada!");
        }
        System.out.println("Fim do programa");
    }
}
$ java Main.java
Aprovada!
Fim do programa
if
Palavra reservada: "se". Sempre minúscula.
(nota >= 7.0)
A condição, sempre entre parênteses. Tem de ser uma expressão booleana: algo que dá true ou false.
{ … }
O bloco: as linhas entre as chaves rodam só se a condição for true. Se for false, o Java pula o bloco inteiro.
System.out.println("Fim do programa");
Está fora do bloco, depois do }: roda sempre, seja qual for a nota.

Com nota = 5.5, a saída seria só Fim do programa: a condição dá false e o println("Aprovada!") é pulado. Repare na indentação (os 4 espaços a mais dentro do bloco): o Java não precisa dela, mas é ela que mostra a quem lê o que está dentro do if.


else: senão, faça outra coisa

O else ("senão") vem logo depois do bloco do if e tem o seu próprio bloco, que roda quando a condição é false. Sempre roda exatamente um dos dois blocos, nunca os dois, nunca nenhum.

public class Main {
    public static void main(String[] args) {
        double nota = 5.5;
        if (nota >= 7.0) {
            System.out.println("Aprovada!");
            System.out.println("Parabéns, Ana.");
        } else {
            System.out.println("Reprovada.");
            System.out.println("Faltaram " + (7.0 - nota) + " pontos.");
        }
        System.out.println("Fim do programa");
    }
}
$ java Main.java
Reprovada.
Faltaram 1.5 pontos.
Fim do programa

Um bloco pode ter quantas linhas quiser. Como 5.5 não é >= 7.0, o Java pulou o primeiro bloco e rodou o do else. O else não tem condição: ele é simplesmente "todos os outros casos".


else if: várias opções

Para escolher entre mais de dois caminhos, encadeie else if ("senão, se"). O Java testa as condições de cima para baixo e roda o bloco da primeira que for verdadeira; as de baixo nem são testadas. O else final, opcional, pega o que sobrou.

public class Main {
    public static void main(String[] args) {
        double nota = 7.8;
        if (nota >= 9.0) {
            System.out.println("Conceito A");
        } else if (nota >= 7.0) {
            System.out.println("Conceito B");
        } else if (nota >= 5.0) {
            System.out.println("Conceito C");
        } else {
            System.out.println("Conceito D");
        }
    }
}
$ java Main.java
Conceito B
PassoPerguntaResposta (nota 7.8)O que acontece
1nota >= 9.0?falsePula o bloco, vai para o próximo teste
2nota >= 7.0?trueMostra Conceito B e sai da escada inteira
3nota >= 5.0?(não é testada)
4else(não roda)

Repare que o teste 2 não precisa dizer nota >= 7.0 && nota < 9.0: se chegou ali, é porque o teste 1 já deu false, ou seja, a nota é menor que 9.

A ordem importa

Como vale a primeira condição verdadeira, uma escada na ordem errada dá resultados errados, sem nenhum aviso:

public class Main {
    public static void main(String[] args) {
        double nota = 9.5;
        if (nota >= 5.0) {
            System.out.println("Conceito C");
        } else if (nota >= 7.0) {
            System.out.println("Conceito B");
        } else if (nota >= 9.0) {
            System.out.println("Conceito A");
        }
    }
}
$ java Main.java
Conceito C       « 9.5 merecia A!

9.5 é >= 5.0, então o primeiro teste "pegou" a nota, e os outros nunca foram testados. Com condições do tipo "maior ou igual", comece pela mais exigente (a maior). Na dúvida, pergunte: "um valor que chega até aqui pode cair num teste acima por engano?".

Missão: complete a escada de else if para classificar a temperatura: 30 ou mais, Muito quente; 25 ou mais, Calor; 15 ou mais, Agradável; abaixo disso, Frio. Com 28 graus, a saída deve ser Calor. Teste também com 31, 15 e 10.

public class Main {
    public static void main(String[] args) {
        int temperatura = 28;
        if (temperatura >= 30) {
            System.out.println("Muito quente");
        }
    }
}
public class Main {
    public static void main(String[] args) {
        int temperatura = 28;
        if (temperatura >= 30) {
            System.out.println("Muito quente");
        } else if (temperatura >= 25) {
            System.out.println("Calor");
        } else if (temperatura >= 15) {
            System.out.println("Agradável");
        } else {
            System.out.println("Frio");
        }
    }
}

Blocos e chaves: não economize

Se o bloco tem uma única linha, o Java permite omitir as chaves: if (x > 0) System.out.println("positivo");. Parece prático, mas é uma armadilha. Sem chaves, o if controla só a próxima instrução, não importa como as linhas estão alinhadas. Veja o que acontece quando alguém acrescenta uma linha depois:

Este código tem um erro de propósito (de lógica: ele compila).

public class Main {
    public static void main(String[] args) {
        int saldo = 50;
        if (saldo < 0)
            System.out.println("Saldo negativo!");
            System.out.println("Cobrando juros...");
        System.out.println("Saldo: " + saldo);
    }
}
$ java Main.java
Cobrando juros...    « o saldo é positivo e mesmo assim cobrou juros!
Saldo: 50

A indentação engana: parece que as duas linhas estão "dentro" do if. Para o Java, só a primeira está. A segunda é uma linha comum, que roda sempre. O javac não avisa nada. Com chaves, não há dúvida:

        if (saldo < 0) {
            System.out.println("Saldo negativo!");
            System.out.println("Cobrando juros...");
        }
Regra do guia: sempre use chaves

Mesmo com uma linha só. É a recomendação dos guias de estilo de Java (e a causa de falhas de segurança famosas em programas reais). As IDEs colocam as chaves para você.

O ; que esvazia o if

Um ponto e vírgula logo depois dos parênteses é outro erro silencioso. Este código tem um erro de propósito.

public class Main {
    public static void main(String[] args) {
        int saldo = 50;
        if (saldo < 0); {
            System.out.println("Saldo negativo!");
        }
        System.out.println("Saldo: " + saldo);
    }
}
$ java Main.java
Saldo negativo!      « com saldo 50?!
Saldo: 50

O ; é uma instrução vazia ("não faça nada"), e é ela que o if controla. O bloco { … } que vem depois fica solto e roda sempre. O javac compila normalmente, mas, se você pedir todos os avisos com -Xlint:all, ele aponta o problema:

$ javac -Xlint:all Main.java
Main.java:4: warning: [empty] empty statement after if
        if (saldo < 0); {
                      ^
1 warning

empty statement after if = "instrução vazia depois do if". Um warning (aviso) não impede a compilação, mas quase sempre indica um erro. As IDEs mostram esse aviso automaticamente.

Missão: rode e veja os juros cobrados de um saldo positivo. Coloque as chaves que faltam para que as duas mensagens fiquem dentro do if. A saída correta é só Saldo: 50.

public class Main {
    public static void main(String[] args) {
        int saldo = 50;
        if (saldo < 0)
            System.out.println("Saldo negativo!");
            System.out.println("Cobrando juros...");
        System.out.println("Saldo: " + saldo);
    }
}
public class Main {
    public static void main(String[] args) {
        int saldo = 50;
        if (saldo < 0) {
            System.out.println("Saldo negativo!");
            System.out.println("Cobrando juros...");
        }
        System.out.println("Saldo: " + saldo);
    }
}

Variáveis criadas dentro do bloco

Uma variável declarada dentro de um bloco só existe ali dentro; ao chegar no }, ela some. Esse "alcance" se chama escopo (aula própria em Escopo). Se você precisa do valor depois do if, declare a variável antes e só atribua dentro:

public class Main {
    public static void main(String[] args) {
        double nota = 8.0;
        String situacao;
        if (nota >= 7.0) {
            situacao = "aprovada";
        } else {
            situacao = "reprovada";
        }
        System.out.println("Situação: " + situacao);
    }
}
$ java Main.java
Situação: aprovada

Com if e else, o javac sabe que situacao sempre recebe um valor. Sem o else, ele reclama (veja "Quando dá errado"). Para casos simples como este, o operador ternário faz o mesmo em uma linha.


if dentro de if

Um bloco pode conter qualquer código, inclusive outro if. Isso se chama if aninhado (como ninhos, um dentro do outro). Use quando a segunda pergunta só faz sentido depois da primeira:

public class Main {
    public static void main(String[] args) {
        int idade = 20;
        boolean temIngresso = true;
        if (idade >= 18) {
            if (temIngresso) {
                System.out.println("Pode entrar no show.");
            } else {
                System.out.println("Compre um ingresso na bilheteria.");
            }
        } else {
            System.out.println("Show proibido para menores.");
        }
    }
}
$ java Main.java
Pode entrar no show.

Cada else pertence ao if cujo bloco acabou de fechar: o primeiro else responde a if (temIngresso); o segundo, a if (idade >= 18). A indentação mostra isso a olho nu. Evite mais de dois ou três níveis: quando os ninhos ficam fundos, uma condição combinada com && ou uma escada de else if costuma ficar mais clara.


Comparar Strings no if

A regra da aula Strings vale em dobro aqui: no if, compare textos com equals. O == compila e às vezes funciona, o que torna o erro traiçoeiro:

public class Main {
    public static void main(String[] args) {
        String digitado = "SIM";
        String resposta = digitado.toLowerCase();
        System.out.println(resposta);
        if (resposta == "sim") {
            System.out.println("== disse: confirmado");
        } else {
            System.out.println("== disse: NÃO confirmado");
        }
        if (resposta.equals("sim")) {
            System.out.println("equals disse: confirmado");
        }
        if (digitado.equalsIgnoreCase("sim")) {
            System.out.println("equalsIgnoreCase disse: confirmado");
        }
    }
}
$ java Main.java
sim                                   « o texto é exatamente "sim"…
== disse: NÃO confirmado              « …mas o == diz que não
equals disse: confirmado
equalsIgnoreCase disse: confirmado    « aceita SIM, Sim, sim…

toLowerCase() criou uma String nova durante a execução, e o == compara se são o mesmo objeto, não o mesmo texto. Com dados que vêm do teclado (Scanner), isso acontece sempre.


Formas de decidir: qual usar?

SituaçãoUse
Fazer algo só em um casoif
Dois caminhos (sim / não)if … else
Várias faixas ou condições diferentesif … else if … else
Escolher entre dois valores para guardar ou mostrarOperador ternário cond ? a : b
Comparar uma variável com vários valores exatos (1, 2, 3; "A", "B")switch ou switch com setas
Uma pergunta que só faz sentido depois de outraif aninhado

Quando dá errado

error: 'else' without 'if'

Este código tem um erro de propósito.

public class Main {
    public static void main(String[] args) {
        int idade = 20;
        if (idade >= 18);
            System.out.println("Maior de idade");
        else
            System.out.println("Menor de idade");
    }
}
$ javac Main.java
Main.java:6: error: 'else' without 'if'
        else
        ^
1 error

O que aconteceu: "else sem if". O ; depois do if encerrou o if ali mesmo. O println virou uma linha comum, e o else ficou órfão: só pode vir imediatamente depois do bloco (ou da instrução) de um if. O mesmo erro aparece quando, sem chaves, há duas linhas entre o if e o else, ou quando uma chave } está fora do lugar.

Como resolver: tire o ; e use chaves: if (idade >= 18) { … } else { … }.

error: incompatible types: int cannot be converted to boolean (= no lugar de ==)

Este código tem um erro de propósito.

public class Main {
    public static void main(String[] args) {
        int idade = 20;
        if (idade = 18) {
            System.out.println("Fez 18 anos!");
        }
    }
}
$ javac Main.java
Main.java:4: error: incompatible types: int cannot be converted to boolean
        if (idade = 18) {
                  ^
1 error

O que aconteceu: um = só é atribuição ("guarde 18 em idade"), e o resultado dela é o número 18. O if precisa de um boolean e recebeu um int. Em C, isso compilaria e daria um erro difícil de achar; o Java, por exigir boolean, pega o engano.

Como resolver: para comparar, dois sinais: if (idade == 18).

O = com boolean não dá erro (e sempre entra no if)

Este código tem um erro de propósito (de lógica: ele compila).

public class Main {
    public static void main(String[] args) {
        boolean ligado = false;
        if (ligado = true) {
            System.out.println("Está ligado");
        }
    }
}
$ java Main.java
Está ligado

O que aconteceu: aqui o tipo bate: ligado = true guarda true e o resultado é true, um boolean. O if sempre entra, e ainda estragou o valor de ligado.

Como resolver: nem = nem ==: escreva if (ligado) (veja Expressões Booleanas).

error: '(' expected

Este código tem um erro de propósito.

public class Main {
    public static void main(String[] args) {
        int idade = 20;
        if idade >= 18 {
            System.out.println("Maior de idade");
        }
    }
}
$ javac Main.java
Main.java:4: error: '(' expected
        if idade >= 18 {
          ^
Main.java:4: error: ')' expected
        if idade >= 18 {
                      ^
2 errors

O que aconteceu: em Java, os parênteses da condição são obrigatórios (em Python e Go, não).

Como resolver: if (idade >= 18) {.

error: cannot find symbol … symbol: variable situacao

Este código tem um erro de propósito.

public class Main {
    public static void main(String[] args) {
        double nota = 8.0;
        if (nota >= 7.0) {
            String situacao = "aprovada";
        } else {
            String situacao = "reprovada";
        }
        System.out.println(situacao);
    }
}
$ javac Main.java
Main.java:9: error: cannot find symbol
        System.out.println(situacao);
                           ^
  symbol:   variable situacao
  location: class Main
1 error

O que aconteceu: as duas variáveis situacao foram criadas dentro dos blocos e deixaram de existir no }. Na linha 9, não há nenhuma situacao visível.

Como resolver: declare antes do if (String situacao;) e só atribua dentro dos blocos, como no exemplo da seção "Variáveis criadas dentro do bloco".

error: variable situacao might not have been initialized

Este código tem um erro de propósito.

public class Main {
    public static void main(String[] args) {
        double nota = 8.0;
        String situacao;
        if (nota >= 7.0) {
            situacao = "aprovada";
        }
        System.out.println(situacao);
    }
}
$ javac Main.java
Main.java:8: error: variable situacao might not have been initialized
        System.out.println(situacao);
                           ^
1 error

O que aconteceu: "a variável pode não ter sido inicializada". Se a nota fosse menor que 7, ninguém daria valor a situacao. O javac analisa todos os caminhos possíveis e não deixa usar uma variável que, em algum caminho, ficou sem valor (mesmo que, com 8.0, o caminho seja outro).

Como resolver: acrescente o else que atribui o outro valor, ou dê um valor inicial na declaração.

error: class, interface, annotation type, enum, record, method or field expected
$ javac Main.java
Main.java:10: error: class, interface, annotation type, enum, record, method or field expected
}
^
1 error

O que aconteceu: sobrou uma chave }. Ao fechar blocos de if dentro de if, é fácil perder a conta: o javac chegou a uma } depois do fim da classe e esperava outra coisa ali. Quando falta uma chave, o erro costuma ser reached end of file while parsing.

Como resolver: confira os pares de chaves pela indentação; as IDEs destacam o par de cada chave.


Referência

FormaRoda
if (cond) { A }A, se cond for true
if (cond) { A } else { B }A ou B, nunca os dois
if (c1) { A } else if (c2) { B } else { C }O bloco da primeira condição verdadeira; C se nenhuma
if (c1) { if (c2) { A } }A, se c1 e c2 forem true (o mesmo que if (c1 && c2))
if (texto.equals("x"))Comparação de String (nunca ==)
if (ativo) / if (!ativo)Variável boolean, sem == true

Exercícios

Missão final: calcule o frete de uma loja. Compras de 200 ou mais: Frete grátis. Senão, se o estado for "PE": Frete: R$ 10. Senão: Frete: R$ 25. Com a compra de 150 em PE, a saída deve ser Frete: R$ 10. Use equals para o estado.

public class Main {
    public static void main(String[] args) {
        double compra = 150.0;
        String estado = "PE";
        // escreva o if / else if / else
    }
}
public class Main {
    public static void main(String[] args) {
        double compra = 150.0;
        String estado = "PE";
        if (compra >= 200) {
            System.out.println("Frete grátis");
        } else if (estado.equals("PE")) {
            System.out.println("Frete: R$ 10");
        } else {
            System.out.println("Frete: R$ 25");
        }
    }
}

1. A palavra que falta

Complete para mostrar "Ímpar" quando o número não for par:

if (n % 2 == 0) { System.out.println("Par"); } { System.out.println("Ímpar"); }

2. Comparar texto

Complete a condição para testar se cor é "azul":

if (cor.) { … }

3. Qual a saída?

Com int x = 10;, o que mostra if (x > 5) { System.out.println("A"); } else if (x > 8) { System.out.println("B"); } else { System.out.println("C"); }?

4. Com int x = -3;, o que este código mostra?

if (x > 0)
    System.out.println("positivo");
    System.out.println("fim");

5. Por que if (idade = 18) não compila, com int idade?

6. Numa escada de else if, quantos blocos rodam no máximo?


Resumo

  • if (condição) { … } roda o bloco só quando a condição é true; a condição vai entre parênteses e tem de ser boolean.
  • else é o "senão": exatamente um dos dois blocos roda.
  • else if encadeia opções; vale a primeira verdadeira, então a ordem importa (comece pela mais exigente).
  • Use sempre chaves. Sem elas, o if controla só a próxima linha, e a indentação mente. Nada de ; depois do ).
  • Variável criada dentro do bloco some no }: declare antes se precisar dela depois.
  • Strings no if: equals. Comparação: ==, nunca =.