O que é when em Kotlin?
Resposta rápida: when é a estrutura do Kotlin usada para escolher entre vários casos. Ela substitui o switch de outras linguagens, mas também compara ranges com in, verifica tipos com is, agrupa valores e aceita condições booleanas. Como é uma expressão, o when pode retornar diretamente o valor de uma variável ou função. Quando o compilador não consegue garantir que todos os casos foram tratados, inclua else.
val mensagem = when (status) {
200 -> "Sucesso"
in 400..499 -> "Erro do cliente"
in 500..599 -> "Erro do servidor"
else -> "Status desconhecido"
}
O when funciona tanto como expressão, retornando um valor, quanto para apenas executar um bloco. Ele é mais flexível que o switch tradicional de Java ou C porque suporta valores, ranges, testes de tipo, enums, sealed classes e condições compostas.
| Necessidade | Forma recomendada |
|---|---|
| Comparar valores exatos | when (valor) { 1 -> ... } |
| Agrupar opções | "a", "e", "i" -> ... |
| Testar um intervalo | in 1..10 -> ... |
| Verificar um tipo | is String -> ... |
| Avaliar condições diferentes | when { condição -> ... } |
| Modelar estados fechados | when com enum ou sealed class |
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 inválida"
}
}
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 útil"
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
Quando cada branch depende de uma condição booleana diferente, use when sem argumento. Nesse formato, os branches são avaliados de cima para baixo, como uma cadeia organizada de if e else if:
fun classificarIdade(idade: Int, temDocumento: Boolean): String = when {
idade < 0 -> "Idade inválida"
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
}
Isso não é uma sintaxe especial de guarda: é o when sem valor de entrada. A ordem importa, porque a primeira condição verdadeira vence. Coloque condições mais específicas antes das mais gerais.
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 comoin 200..299para sucesso,in 400..499para erros do cliente ein 500..599para erros do servidor, tornando o código muito mais legível do que uma cadeia deif-else. - Navegação em aplicativos Android: Em apps com Jetpack Compose ou Fragments, o
whencombinado 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
whencom 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
whencom sealed classes, garantindo que toda transição de estado é tratada explicitamente.
Boas Práticas
- Prefira
whencomo expressão: Sempre que possível, usewhenretornando um valor diretamente comval 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
whenpermite 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
whena cadeias longas deif-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 queif-else if-else. - Aproveite o smart cast: Dentro de branches com
is, o Kotlin já faz o cast automaticamente. Não useasmanualmente após verificar o tipo comis— isso é redundante e polui o código.
Erros Comuns
- Esquecer o
elseem expressõeswhen: Quandowhené usado como expressão (retornando valor), oelseé 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
whenavalia 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 Numberantes deis Intfaz com que inteiros caiam sempre no branch deNumber. - Não aproveitar ranges: Escrever
1 -> ..., 2 -> ..., 3 -> ...quando todos retornam o mesmo valor é desnecessário. Usein 1..3 -> ...para simplificar. O mesmo vale para verificações como"a", "b", "c" -> .... - Usar
whenpara apenas dois casos: Se você tem apenas duas opções (verdadeiro/falso, presente/ausente), um simplesif-elseé mais claro e conciso do que umwhen. Usewhenquando 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 é o equivalente ao switch? Sim. Para quem migra de Java, JavaScript, C ou C#, when cumpre o papel de selecionar entre vários casos, mas também oferece ranges com in, testes de tipo com is, condições arbitrárias sem argumento e retorno de valor. Em Java moderno, switch também pode ser usado como expressão; ainda assim, a sintaxe e a integração de when com smart casts e tipos selados são próprias do Kotlin.
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 com
whenpara garantir exaustividade. - Enum — enumerações que permitem um
whenexaustivo semelse. - val — variáveis somente leitura, ideais para receber o resultado de uma expressão
when. - Smart Cast — mecanismo que permite usar o tipo verificado com
isdentro dos branches. - Estruturas condicionais em Kotlin — tutorial para praticar
if,elseewhenem conjunto.