W3docs

Expressões switch Modernas em Java

Use expressões switch modernas em Java com rótulos de seta, yield, exaustividade e correspondência de padrões.

A instrução switch tradicional faz parte do Java desde a versão 1.0, mas carregava muito peso: bugs de fall-through, instruções break repetitivas e nenhuma forma de produzir um valor. As expressões switch, finalizadas no Java 14, resolvem tudo isso. Elas transformam o switch de uma instrução de controle de fluxo desajeitada em uma expressão concisa que produz valores.

Este capítulo percorre o switch moderno: rótulos de seta, a palavra-chave yield, casos com múltiplos rótulos, verificação de exaustividade e exatamente como a nova forma difere da instrução que você já pode conhecer.

De Instrução a Expressão

A instrução switch clássica executa efeitos colaterais e depende de break para impedir o fall-through. Esqueça um break e a execução cai silenciosamente no próximo caso — uma fonte notória de bugs.

// Traditional switch statement (error-prone)
String kind;
switch (day) {
    case SATURDAY:
    case SUNDAY:
        kind = "weekend";
        break;          // forget this and you fall through
    default:
        kind = "weekday";
}

Uma expressão switch reduz isso a uma única atribuição. A forma com seta (->) nunca cai em fall-through, portanto nenhum break é necessário.

// Modern switch expression
String kind = switch (day) {
    case SATURDAY, SUNDAY -> "weekend";
    default               -> "weekday";
};

Todo o switch agora avalia para um valor, que você pode atribuir, retornar ou passar como argumento diretamente.

Rótulos de Seta e Casos com Múltiplos Rótulos

O rótulo de seta case L -> associa um rótulo (ou vários, separados por vírgula) a uma única ação. Apenas o ramo correspondente é executado — não há fall-through com o qual se preocupar.

int numLetters = switch (month) {
    case JANUARY, JUNE, JULY          -> 4;
    case FEBRUARY, MARCH, APRIL, MAY  -> 5;
    case SEPTEMBER, OCTOBER, NOVEMBER, DECEMBER -> switchOnLength(month);
    case AUGUST                       -> 6;
};

Agrupar rótulos com vírgulas substitui o antigo truque de empilhar linhas case vazias para compartilhar um corpo, e lê-se com muito mais clareza.

Recursoswitch tradicional (case L:)switch moderno (case L ->)
Fall-throughSim, a menos que você use breakNão, cada ramo é isolado
Produz um valorNãoSim (é uma expressão)
Múltiplos rótulosLinhas case vazias empilhadasSeparados por vírgula em uma linha
Escopo de variáveisCompartilhado em todo o blocoLocal a cada bloco de ramo

Blocos e a Palavra-chave yield

Quando um ramo precisa de mais do que uma única expressão, use um bloco { ... } e retorne seu valor com yield. A palavra-chave yield é para uma expressão switch o que return é para um método: ela fornece o valor que o ramo produz.

int gradePoints = switch (grade) {
    case 'A' -> 4;
    case 'B' -> 3;
    default -> {
        log("Unknown grade: " + grade);
        yield 0;          // the value this branch evaluates to
    }
};

Você ainda pode usar rótulos de dois pontos com yield se preferir a sintaxe antiga, mas a forma com seta é a escolha idiomática moderna e evita completamente o fall-through acidental.

Exaustividade e default

Uma expressão switch deve ser exaustiva: toda entrada possível deve ser tratada, pois a expressão precisa produzir um valor independentemente do que aconteça. Para a maioria dos tipos, isso é satisfeito com um ramo default. Para um enum, o compilador pode verificar a exaustividade diretamente — se você cobrir todas as constantes, default se torna opcional.

// No default needed: all enum constants are covered,
// so the compiler knows the switch is exhaustive.
boolean isWeekend = switch (day) {
    case SATURDAY, SUNDAY -> true;
    case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> false;
};

Se você omitir uma constante e não houver default, o código não compilará. Essa rede de segurança em tempo de compilação é um dos maiores ganhos práticos em relação à instrução antiga, que silenciosamente deixava uma variável sem atribuição.

Correspondência de Padrões em switch

Versões recentes do Java estendem o switch para que cada case possa corresponder ao tipo do valor, não apenas a uma constante. Isso é a correspondência de padrões para switch (uma prévia no Java 17–20, finalizada no Java 21). Em vez de escrever uma cadeia de verificações instanceof e conversões, você rotula cada ramo com um padrão de tipo e vincula uma variável na mesma etapa.

static String describe(Object obj) {
    return switch (obj) {
        case Integer i -> "int " + i;            // matches and binds i
        case String s  -> "string of length " + s.length();
        case null      -> "nothing";             // null can be its own label
        default        -> "something else";
    };
}

A correspondência de padrões é especialmente poderosa com records e tipos selados: quando um switch cobre todos os subtipos permitidos de um tipo selado, o compilador o trata como exaustivo, portanto você pode eliminar o default completamente.

sealed interface Shape permits Circle, Square {}
record Circle(double radius) implements Shape {}
record Square(double side)   implements Shape {}

static double area(Shape shape) {
    return switch (shape) {          // no default: all permitted types covered
        case Circle c -> Math.PI * c.radius() * c.radius();
        case Square s -> s.side() * s.side();
    };
}
Nota
Antes da correspondência de padrões, um switch só podia testar um enum, um tipo integral ou uma String. Os padrões de tipo permitem que o switch funcione em qualquer tipo de referência, tornando-o um substituto natural para longas escalas if/else if.

Um Exemplo Completo Desenvolvido

O programa abaixo reúne as peças: rótulos de seta com casos de múltiplos rótulos, um bloco yield para um ramo computado, cobertura exaustiva de enum e uma expressão switch atribuída diretamente a uma variável.

java— editable, runs on the server

O que extrair da execução:

  • kind() retorna o resultado de uma expressão switch diretamente — MONDAY e FRIDAY imprimem weekday, SATURDAY imprime weekend, tudo sem um único break.
  • Os dois ramos de seta em kind() cobrem todas as sete constantes do enum, portanto o switch é exaustivo e não precisa de default.
  • Em letterGrade(), as pontuações 95, 83, 71 e 64 mapeiam claramente através de rótulos de seta para 4, 3, 2 e 1 pontos de nota.
  • A pontuação 42 atinge o bloco default, que primeiro imprime (failing score 42) pela linha de efeito colateral e depois retorna 0 com yield — mostrando como um ramo de bloco pode fazer trabalho antes de produzir seu valor.
  • O switch final atribui TWO diretamente à variável label, provando que uma expressão switch é um valor que você pode armazenar, não apenas controle de fluxo.

Quando Usar Qual Forma

  • Opte por uma expressão switch (case L ->) sempre que o objetivo for calcular e retornar um único valor. Ela é exaustiva, livre de fall-through e lê-se como uma única atribuição.
  • Uma instrução switch tradicional ainda faz sentido quando cada ramo é puramente um efeito colateral (registro, despacho) e não há valor a produzir.
  • Use padrões de tipo quando estiver ramificando no tipo de tempo de execução de um objeto, particularmente entre as constantes de um enum ou os subtipos de um tipo selado.

Para o histórico completo e a forma com rótulo de dois pontos, consulte expressões switch Java.

Prática

Prática
Em uma expressão switch moderna, qual palavra-chave produz um valor de dentro de um bloco (a forma { ... } de um ramo)?
Em uma expressão switch moderna, qual palavra-chave produz um valor de dentro de um bloco (a forma { ... } de um ramo)?
Was this page helpful?