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. Não existe break: cada branch é independente e o primeiro que corresponde encerra o when. 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 -> ... |
| Testar pertencimento a uma coleção | in listaPermitida -> ... |
| Excluir um intervalo ou coleção | !in 1..10 -> ... |
| Verificar um tipo | is String -> ... |
| Avaliar condições diferentes | when { condição -> ... } |
| Guardar o valor avaliado | when (val r = calcular()) { ... } |
| 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.
Capturando o valor com when (val ...)
Quando o valor avaliado vem de uma chamada de função, declare-o direto no when. A função é executada uma única vez, a variável fica disponível em todos os branches e não vaza para o escopo de fora:
data class Usuario(val nome: String)
fun buscarUsuario(id: Int): Usuario? =
if (id == 1) Usuario("Karina") else null
fun carregarPerfil(id: Int): String = when (val resposta = buscarUsuario(id)) {
null -> "Usuário $id não encontrado"
else -> "Bem-vindo, ${resposta.nome}"
}
fun main() {
println(carregarPerfil(1)) // Bem-vindo, Karina
println(carregarPerfil(9)) // Usuário 9 não encontrado
}
Esse formato evita o erro clássico de chamar a mesma função em vários branches — o que, além de lento, pode devolver resultados diferentes a cada chamada.
Testando coleções com in e !in
O operador in do when não serve só para ranges numéricos: ele funciona com qualquer coleção ou tipo que implemente contains. Use !in para o caso oposto:
val metodosSeguros = setOf("GET", "HEAD", "OPTIONS")
val metodosConhecidos = metodosSeguros + setOf("POST", "PUT", "PATCH", "DELETE")
fun classificarMetodo(metodo: String): String = when (metodo) {
in metodosSeguros -> "Leitura: dispensa token"
!in metodosConhecidos -> "Método desconhecido: rejeitar"
else -> "Escrita: exige token"
}
fun main() {
println(classificarMetodo("GET")) // Leitura: dispensa token
println(classificarMetodo("POST")) // Escrita: exige token
println(classificarMetodo("TRACE")) // Método desconhecido: rejeitar
}
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.
O Kotlin também vem desenvolvendo guard conditions, que permitem combinar um assunto com uma condição extra no mesmo branch (is Erro if resultado.codigo == 500 -> ...). Esse recurso chegou como preview experimental no Kotlin 2.1 e precisa ser habilitado por flag do compilador, então ele ainda não é a forma padrão de escrever esse código. Enquanto isso não estabiliza na sua versão, a maneira portável de expressar a mesma ideia é aninhar um if dentro do branch ou usar when sem argumento, como nos exemplos acima. Confira sempre o comportamento na versão de Kotlin configurada no seu build.gradle.kts.
Migrando do switch do Java
Quem vem de Java, C# ou JavaScript tende a traduzir o switch linha a linha — e isso gera código mais verboso do que o necessário. Use este mapa direto:
switch (Java clássico) | when (Kotlin) |
|---|---|
case 1: + break; | 1 -> ... (sem break, não há fall-through) |
case 1: case 2: empilhados | 1, 2 -> ... |
default: | else -> ... |
if encadeado por faixa de valores | in 1..10 -> ... |
instanceof + cast manual | is String -> ... com smart cast |
Variável atribuída em cada case | val x = when (...) { ... } como expressão |
switch só aceita alguns tipos | when aceita qualquer tipo, inclusive Any |
A diferença prática mais importante é a ausência de fall-through: no Java, esquecer o break faz a execução escorregar para o próximo case, um dos bugs mais antigos da linguagem. No Kotlin isso não existe, então não procure por break dentro do when.
when x if: como escolher rapidamente
| Situação | Melhor escolha | Por quê |
|---|---|---|
| Dois caminhos (sim/não, null/não-null) | if / else | Mais curto e direto |
| Três ou mais valores do mesmo dado | when (valor) | Um branch por caso, sem encadear else if |
| Faixas ou listas de valores | when com in | in 7..9 -> ... substitui várias comparações |
| Teste de tipo com conversão | when com is | Smart cast elimina o cast manual |
| Estados de enum ou sealed class | when | Compilador garante exaustividade |
| Condições independentes sobre variáveis diferentes | when { ... } sem argumento | Cada branch declara sua própria condição |
Regra prática: se você está comparando o mesmo valor contra várias possibilidades, é when; se está testando uma condição booleana pontual, é if.
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. - Procurar por
breakou temer fall-through: Vindo do Java, é comum adicionarbreakpor reflexo ou duplicar lógica achando que o branch seguinte também será executado. No Kotlin owhenjá para no primeiro branch correspondente. - Chamar a mesma função em vários branches: Se o valor avaliado vem de uma chamada, use
when (val resultado = chamada())em vez de repetirchamada()em cada condição. - 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.
Posso declarar uma variável dentro do when? Sim, com when (val resultado = chamada()) { ... }. O escopo de resultado é limitado ao when, o que mantém o restante da função limpo e garante uma única execução da chamada.
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".
Próximos Passos
- Estruturas condicionais em Kotlin — pratique
if,whene expressões condicionais em exercícios guiados - Tutorial de sealed classes — aprofunde a dupla sealed class +
whenque garante exaustividade - Convertendo código Java para Kotlin — veja na prática como um
switchdo Java virawhenno VS Code - Boas práticas em Kotlin — convenções de estilo que deixam suas expressões
whenidiomáticas - Glossário de Kotlin — explore todos os termos da linguagem, de
vala coroutines
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.