O que é when em Kotlin?

O when é a versão turbinada do switch em Kotlin. Ele funciona tanto como expressão (retorna valor) quanto como instrução (executa blocos). É mais poderoso e flexível que o switch de Java ou C — suporta ranges, verificações de tipo, condições complexas é muito mais.

Sintaxe básica

fun classificarNota(nota: Int): String {
    return when (nota) {
        10 -> "Perfeito!"
        in 7..9 -> "Aprovado"
        in 5..6 -> "Recuperação"
        in 0..4 -> "Reprovado"
        else -> "Nota invalida"
    }
}

fun main() {
    println(classificarNota(8))  // Aprovado
    println(classificarNota(3))  // Reprovado
}

When como expressão

Quando when é usado como expressão (retornando valor), o else é obrigatório — a menos que o compilador consiga verificar que todos os casos estão cobertos:

enum class DiaSemana { SEG, TER, QUA, QUI, SEX, SAB, DOM }

fun tipoDeDia(dia: DiaSemana) = when (dia) {
    DiaSemana.SEG, DiaSemana.TER, DiaSemana.QUA,
    DiaSemana.QUI, DiaSemana.SEX -> "Dia util"
    DiaSemana.SAB, DiaSemana.DOM -> "Fim de semana"
}

Verificação de tipo com is

fun descrever(obj: Any): String = when (obj) {
    is String -> "Texto com ${obj.length} caracteres"
    is Int -> "Número inteiro: $obj"
    is List<*> -> "Lista com ${obj.size} itens"
    is Boolean -> if (obj) "Verdadeiro" else "Falso"
    else -> "Tipo desconhecido"
}

fun main() {
    println(descrever("Kotlin"))        // Texto com 6 caracteres
    println(descrever(42))              // Número inteiro: 42
    println(descrever(listOf(1, 2, 3))) // Lista com 3 itens
}

Repare que dentro de cada branch, o smart cast já aplica o tipo automaticamente.

When sem argumento

Você pode usar when sem argumento, funcionando como uma cadeia de if-else:

fun avaliarTemperatura(temp: Double): String = when {
    temp < 0 -> "Congelante!"
    temp < 15 -> "Tá frio, hein"
    temp < 25 -> "Temperatura agradável"
    temp < 35 -> "Tá quente"
    else -> "Derretendo!"
}

fun main() {
    println(avaliarTemperatura(28.0)) // Tá quente
}

When com sealed class

A combinação com sealed class é perfeita — o compilador garante que todos os casos são tratados, sem precisar de else. Essa é uma das duplas mais poderosas do Kotlin pra modelar estados e resultados.

sealed class Resultado {
    data class Sucesso(val dados: String) : Resultado()
    data class Erro(val mensagem: String) : Resultado()
    data object Carregando : Resultado()
}

fun tratarResultado(resultado: Resultado): String = when (resultado) {
    is Resultado.Sucesso -> "Dados recebidos: ${resultado.dados}"
    is Resultado.Erro -> "Falha: ${resultado.mensagem}"
    is Resultado.Carregando -> "Aguarde, carregando..."
}

fun main() {
    val r1 = Resultado.Sucesso("Lista de usuários")
    val r2 = Resultado.Erro("Timeout na conexão")
    val r3 = Resultado.Carregando

    println(tratarResultado(r1)) // Dados recebidos: Lista de usuários
    println(tratarResultado(r2)) // Falha: Timeout na conexão
    println(tratarResultado(r3)) // Aguarde, carregando...
}

Como Resultado é uma sealed class, o compilador sabe que existem exatamente três subtipos. Se você adicionar um novo subtipo no futuro, o compilador vai avisar em todos os when que ficaram incompletos.

When com múltiplas condições e guardas

A partir do Kotlin 1.9, o when ganhou suporte a guard conditions, que permitem adicionar condições extras a cada branch usando if.

fun classificarIdade(idade: Int, temDocumento: Boolean): String = when {
    idade < 0 -> "Idade invalida"
    idade < 12 -> "Criança"
    idade < 18 && temDocumento -> "Adolescente com documento"
    idade < 18 -> "Adolescente sem documento"
    idade < 60 -> "Adulto"
    else -> "Idoso"
}

fun main() {
    println(classificarIdade(15, true))  // Adolescente com documento
    println(classificarIdade(15, false)) // Adolescente sem documento
    println(classificarIdade(45, true))  // Adulto
}

Casos de Uso no Mundo Real

  • Tratamento de respostas HTTP: O when é ideal para tratar diferentes códigos de status HTTP. Você pode agrupar ranges como in 200..299 para sucesso, in 400..499 para erros do cliente e in 500..599 para erros do servidor, tornando o código muito mais legível do que uma cadeia de if-else.
  • Navegação em aplicativos Android: Em apps com Jetpack Compose ou Fragments, o when combinado com sealed classes é o padrão para decidir qual tela exibir com base no estado atual de navegação.
  • Parsing de formatos de dados: Ao processar dados de diferentes formatos (JSON, XML, CSV), o when com verificação de tipo (is) permite tratar cada formato de maneira específica com smart cast automático.
  • Máquinas de estado: Sistemas como processamento de pedidos (novo, pago, enviado, entregue, cancelado) se beneficiam enormemente do when com sealed classes, garantindo que toda transição de estado é tratada explicitamente.

Boas Práticas

  • Prefira when como expressão: Sempre que possível, use when retornando um valor diretamente com val resultado = when { ... }. Isso elimina a necessidade de variáveis mutáveis e torna o código mais funcional.
  • Use sealed classes em vez de else: Quando você tem um conjunto fixo de tipos ou estados, modele-os com sealed classes. O compilador vai garantir que todos os casos sejam tratados, e qualquer novo caso adicionado no futuro será detectado em tempo de compilação.
  • Agrupe valores relacionados: O when permite agrupar múltiplos valores na mesma branch usando vírgula, como 'a', 'e', 'i', 'o', 'u' -> "vogal". Use isso para evitar duplicação de lógica.
  • Prefira when a cadeias longas de if-else: Quando você tem três ou mais condições sobre o mesmo valor, when é quase sempre mais legível e menos propenso a erros do que if-else if-else.
  • Aproveite o smart cast: Dentro de branches com is, o Kotlin já faz o cast automaticamente. Não use as manualmente após verificar o tipo com is — isso é redundante e polui o código.

Erros Comuns

  • Esquecer o else em expressões when: Quando when é usado como expressão (retornando valor), o else é obrigatório se os casos não são exaustivos. O compilador vai apontar o erro, mas é comum esquecer durante a escrita inicial.
  • Ordem errada dos branches: O when avalia os branches de cima para baixo e executa o primeiro que corresponder. Se você colocar um branch mais genérico antes de um mais específico, o específico nunca será alcançado. Por exemplo, is Number antes de is Int faz com que inteiros caiam sempre no branch de Number.
  • Não aproveitar ranges: Escrever 1 -> ..., 2 -> ..., 3 -> ... quando todos retornam o mesmo valor é desnecessário. Use in 1..3 -> ... para simplificar. O mesmo vale para verificações como "a", "b", "c" -> ....
  • Usar when para apenas dois casos: Se você tem apenas duas opções (verdadeiro/falso, presente/ausente), um simples if-else é mais claro e conciso do que um when. Use when quando há três ou mais caminhos.
  • Não tratar todos os casos de um enum: Mesmo quando o when é usado como instrução (sem retornar valor), é boa prática tratar todos os valores do enum. Isso evita bugs silenciosos quando novos valores são adicionados ao enum no futuro.

Perguntas Frequentes

when substitui completamente o switch do Java? Sim, e vai muito além. O when do Kotlin suporta verificação de tipo com is, ranges com in, condições arbitrárias (quando usado sem argumento), e funciona como expressão. O switch do Java é limitado a valores constantes de tipos primitivos, enums e strings.

Posso usar when sem chaves nas branches? Sim. Se a branch tem apenas uma expressão, não precisa de chaves: 10 -> "dez". Mas se precisa de múltiplas instruções, envolva com chaves e a última expressão será o valor de retorno da branch.

O when é mais performático que if-else? Depende do caso. Para comparações com valores constantes de Int ou String, o compilador pode gerar um tableswitch ou lookupswitch no bytecode, que é mais eficiente que uma série de comparações. Para condições complexas ou when sem argumento, a performance é equivalente a if-else.

Como funciona o when com valor de retorno em blocos? Quando uma branch do when usa chaves para múltiplas instruções, a última expressão do bloco é o valor retornado. Por exemplo: in 1..10 -> { println("Processando"); "válido" } retorna "válido".

Termos Relacionados

  • Sealed Class — Classes seladas que combinam perfeitamente com when para garantir exaustividade.
  • Enum — Enumerações que permitem when exaustivo sem else.
  • val — Variáveis somente leitura, ideais para receber o resultado de uma expressão when.
  • Smart Cast — O mecanismo que permite usar o tipo verificado com is dentro das branches do when.