Capítulo 02: Estrutura Condicional (if-else, switch expressions) e Operador Ternário
Especialização em Sistemas com Java 21 LTS • Módulo de Fundamentos
🗺️ Mapa Conceitual do Tópico
flowchart TD
A["Fluxo de Execução"] --> B{"Expressão Booleana?"}
B -->|true| C["Ramo Verdadeiro (if / then)"]
B -->|false| D["Ramo Falso (else / default)"]
subgraph MECANISMOS["Evolução das Estruturas Condicionais"]
E["if-else & Ternário ?:"]
F["Switch Tradicional (break / fall-through)"]
G["Switch Expressions (-> e yield)"]
H["Pattern Matching com Guardas (when)"]
end
subgraph JVM["Otimizações no Bytecode"]
I["Branch Prediction CPU"]
J["tableswitch (O(1) Jump Table)"]
K["lookupswitch (O(log n) Binary Search)"]
end
C & D --> MECANISMOS
MECANISMOS --> JVM
style A fill:#e1f5fe,stroke:#03a9f4,stroke-width:2px
style B fill:#fff3e0,stroke:#ff9800,stroke-width:2px
style G fill:#ede7f6,stroke:#7e57c2,stroke-width:2px
style H fill:#e8f5e9,stroke:#4caf50,stroke-width:2px
🔀 1. Lógica Condicional e Avaliação de Curto-Circuito
No Java, qualquer estrutura condicional if exige estritamente um tipo boolean primitivo ou sua wrapper java.lang.Boolean. Ao contrário de linguagens como C ou JavaScript, inteiros não são convertidos implicitamente em booleanos (if (1) resulta em erro de compilação).
Operadores Lógicos e Avaliação de Curto-Circuito:
- Curto-Circuito Lógico (
&&e||): Na conjunçãoA && B, seAforfalse,Bnunca é avaliado. Na disjunçãoA || B, seAfortrue,Bé ignorado. Isso previne falhas graves como acessar métodos em ponteiros nulos:if (cliente != null && cliente.isAtivo()) { ... } // Seguro contra NullPointerException - Operadores Bitwise em Booleanos (
&e|): Forçam a avaliação de ambos os operandos, independentemente do resultado do primeiro. Devem ser evitados em desvios condicionais devido à perda de performance e risco de exceção.
⚖️ 2. Operador Ternário e Regras de Tipo do Compilador
O operador ternário (condicao ? expr1 : expr2) é uma expressão que produz um valor direto. Seu tipo de retorno é inferido pelo compilador com base no menor supertipo comum entre expr1 e expr2.
Armadilha Crítica: Auto-unboxing e NullPointerException
Quando se misturam tipos primitivos e wrappers num operador ternário, a JVM aplica regras de promoção numérica da JLS (Java Language Specification), forçando o unboxing:
// PERIGO: Lança NullPointerException em tempo de execução!
Integer valorNulo = null;
Double resultado = (flag) ? 1.0 : valorNulo; // valorNulo sofre unboxing para double!
🎯 3. Switch Expressions Modernas (Java 14/17/21)
O Java clássico tratava o switch como uma instrução imperativa sujeita ao infame erro de fall-through (esquecimento da palavra-chave break). Desde o Java 14 e consolidado no Java 21 LTS, o switch pode ser utilizado como uma expressão que retorna valor:
1. Sintaxe com Seta (->)
Elimina completamente o fall-through e permite múltiplos rótulos (multi-labels) separados por vírgula:
String tipoDia = switch (diaSemana) {
case 1, 7 -> "Fim de Semana";
case 2, 3, 4, 5, 6 -> "Dia Comercial";
default -> throw new IllegalArgumentException("Dia inválido: " + diaSemana);
};
2. A Palavra-Chave yield
Quando um ramo do switch necessita de um bloco multi-linhas de instruções (cálculos, logs, validações), utiliza-se yield para produzir o valor resultante do bloco:
double tarifa = switch (categoria) {
case "PREMIUM" -> 0.05;
case "STANDARD" -> {
logger.info("Aplicando cálculo dinâmico para Standard");
double base = calcularTaxaBase();
yield base * 1.15;
}
default -> 0.20;
};
🛡️ 4. Pattern Matching para Switch e Cláusulas when (Java 21 LTS)
O Java 21 estabilizou o Pattern Matching para Switch (JEP 441), permitindo alternar fluxos com base no tipo dinâmico do objeto, extrair a variável local tipada sem casting explícito e incluir filtros booleanos refinados com a cláusula de guarda when:
public static String formatarDado(Object obj) {
return switch (obj) {
case null -> "Elemento nulo";
case Integer i when i < 0 -> String.format("Inteiro negativo: (%d)", i);
case Integer i -> String.format("Inteiro positivo: +%d", i);
case Long l -> String.format("Long: %d L", l);
case String s when s.isBlank() -> "String vazia";
case String s -> "Texto: " + s.trim();
default -> "Objeto desconhecido: " + obj.getClass().getSimpleName();
};
}
📊 5. Matriz Comparativa de Estruturas de Decisão
| Recurso | if-else if-else |
switch Clássico |
switch Expression |
Pattern Matching switch |
|---|---|---|---|---|
| Retorno de Expressão | Não (somente statements) | Não | Sim (-> ou yield) |
Sim |
| Risco de Fall-through | Inexistente | Alto (exige break) |
Nenhum | Nenhum |
| Garantia de Exaustividade | Manual (via else) |
Nenhuma | Garantida pelo Compilador | Garantida pelo Compilador |
Suporte a null |
Exige checagem explícita | Lança NPE imediatamente | Lança NPE (a menos de case null) |
Suporte Nativo (case null) |
| Guardas Refinadas | Operadores &&, || |
Não suportado | Não suportado | Cláusula when |
| Bytecode Subjacente | if_icmp*, goto |
tableswitch / lookupswitch |
Otimizado via invokedynamic |
typeSwitch via Bootstrap |
💻 6. Código de Demonstração Corporativo (Java 21 LTS)
package com.empresa.fundamentos;
import java.math.BigDecimal;
import java.math.RoundingMode;
/**
* Motor de análise de crédito e fraude com Java 21 Switch Expressions e Pattern Matching.
*/
public class MotorDecisaoCredito {
public sealed interface Solicitacao permits EmprestimoPessoal, FinanciamentoImobiliario, CartaoCredito {}
public record EmprestimoPessoal(BigDecimal valor, int parcelas, int scoreSerasa) implements Solicitacao {}
public record FinanciamentoImobiliario(BigDecimal valor, BigDecimal rendaMensal, boolean temGarantia) implements Solicitacao {}
public record CartaoCredito(BigDecimal limiteSolicitado, boolean clienteAntigo) implements Solicitacao {}
public enum Parecer { APROVADO, REPROVADO, ANALISE_HUMANA }
public static Parecer avaliarProposta(Solicitacao proposta) {
return switch (proposta) {
case null -> Parecer.REPROVADO;
// Pattern matching com cláusula de guarda (when)
case EmprestimoPessoal ep when ep.scoreSerasa() >= 750 && ep.parcelas() <= 36 -> Parecer.APROVADO;
case EmprestimoPessoal ep when ep.scoreSerasa() < 400 -> Parecer.REPROVADO;
case EmprestimoPessoal ep -> Parecer.ANALISE_HUMANA;
case FinanciamentoImobiliario fi when fi.temGarantia() -> {
BigDecimal parcelaEstimada = fi.valor().divide(new BigDecimal("360"), 2, RoundingMode.HALF_UP);
BigDecimal comprometimento = parcelaEstimada.divide(fi.rendaMensal(), 2, RoundingMode.HALF_UP);
yield (comprometimento.compareTo(new BigDecimal("0.30")) <= 0) ? Parecer.APROVADO : Parecer.ANALISE_HUMANA;
}
case FinanciamentoImobiliario fi -> Parecer.REPROVADO;
case CartaoCredito cc when cc.clienteAntigo() || cc.limiteSolicitado().compareTo(new BigDecimal("5000.00")) <= 0 ->
Parecer.APROVADO;
case CartaoCredito cc -> Parecer.ANALISE_HUMANA;
};
}
public static void main(String[] args) {
Solicitacao proposta1 = new EmprestimoPessoal(new BigDecimal("25000.00"), 24, 820);
Solicitacao proposta2 = new FinanciamentoImobiliario(new BigDecimal("450000.00"), new BigDecimal("12000.00"), true);
Solicitacao proposta3 = null;
System.out.printf("Proposta 1 (Empréstimo) : %s%n", avaliarProposta(proposta1));
System.out.printf("Proposta 2 (Imobiliário) : %s%n", avaliarProposta(proposta2));
System.out.printf("Proposta 3 (Nula) : %s%n", avaliarProposta(proposta3));
}
}