Kotlin: Fluxo de Controle do Kotlin Explicado
Última atualização: 2026-08-26
O fluxo de controle do Kotlin não é apenas controle de fluxo — if e when são expressões com valores de retorno, tornando a lógica de roteamento de pedidos do Charlie ao mesmo tempo segura e concisa.
1. O que Você Aprenderá
ifé uma expressão:val max = if (a > b) a else bwhenem todas as suas formas: múltiplos valores, condições de guarda, vinculação de variáveisfor+ iterador: iterar sobre coleções, intervalos, mapas- Rótulos e retornos:
break@label/continue@label/return@forEach - Charlie em ação: roteamento de pedidos
when(order.status)
2. A História Real de um Arquiteto
(1) Problema: Inferno do if-else Aninhado
O OrderProcessor Java do Charlie usava 5 níveis de if-else aninhados para rotear pedidos por status — 120 linhas apenas para decidir qual pipeline usar. Quando o novo membro Bob entrou, errou nas condições de ramificação 3 vezes, causando incidentes em produção.
(2) Solução: Expressão when do Kotlin
// Limpo, exaustivo, verificado pelo compilador
fun route(order: Order): Pipeline = when (order.status) {
Status.PENDING -> PendingPipeline
Status.CONFIRMED -> if (order.total > 10_000) VipPipeline else StandardPipeline
Status.SHIPPED -> TrackingPipeline
Status.CANCELLED -> RefundPipeline
}
Verificação de exaustividade da expressão
when+ guarda do compilador: um ramo ausente é um erro de compilação. Bob nunca mais poderá introduzir tais bugs.
3. Expressão if
(1) if é uma Expressão com Valor de Retorno
// if como expressão
val max = if (a > b) a else b
// Expressão if com múltiplos ramos
val category = if (total > 10_000) {
println("Pedido de alto valor")
"VIP"
} else if (total > 1_000) {
"PRIORITY"
} else {
"STANDARD"
}
(2) Expressão if Kotlin vs if Java
| Dimensão | Java if | Kotlin if |
|---|---|---|
| Natureza | Instrução | Expressão |
| Valor de retorno | Nenhum | Valor da última expressão |
| Atribuição | Precisa de variável extra | val x = if(...)... |
| Operador ternário | ?: |
if/else |
// Nenhum operador ternário necessário - if/else É o ternário
val discount = if (total > 5_000) 0.15 else if (total > 1_000) 0.10 else 0.0
4. when em Todas as Suas Formas
(1) Expressão when Básica
// Correspondência exata
val label = when (status) {
"PENDING" -> "Aguardando"
"CONFIRMED" -> "Confirmado"
else -> "Desconhecido"
}
// Múltiplos valores em um ramo
val isTerminal = when (status) {
"SHIPPED", "DELIVERED", "CANCELLED" -> true
else -> false
}
(2) Condições de Guarda (when com Guarda)
// Ramos condicionais com 'in' e intervalos
val pipeline = when {
status == "CANCELLED" -> "REFUND"
total > 10_000 && priority == "URGENT" -> "VIP_EXPRESS"
total > 10_000 -> "VIP"
total > 1_000 -> "PRIORITY"
status in listOf("NEW", "PENDING") -> "QUEUE"
else -> "STANDARD"
}
(3) Vinculação de Variáveis e Desestruturação
// Captura com sujeito do when
val result = when (val response = httpClient.call()) {
is Success -> "OK: ${response.data}"
is Error -> "Falhou: ${response.code}"
}
// response é smart-cast e disponível nos ramos
(4) Diagrama de Roteamento de Status de Pedido
flowchart TD
A[when order.status] --> B{PENDING}
A --> C{CONFIRMED}
A --> D{SHIPPED}
A --> E{CANCELLED}
B --> B1[Fila de Espera]
C --> C1{total > 10000?}
C1 -->|Sim| C2[Pipeline VIP]
C1 -->|Não| C3[Pipeline Padrão]
D --> D1[Pipeline de Rastreamento]
E --> E1[Fila de Reembolso]
(5) Comparação das Formas do when
| Forma | Sintaxe | Caso de Uso |
|---|---|---|
| Correspondência exata | when(status) |
Enums / strings / constantes |
| Correspondência de múltiplos valores | "A", "B" -> ... |
Múltiplos valores, mesmo ramo |
| Correspondência por intervalo | in 1..10 |
Intervalos numéricos |
| Correspondência por tipo | is String |
Roteamento por tipo |
| Condições de guarda | when { ... } |
Combinações de condições complexas |
| Vinculação de variáveis | when(val x = ...) |
Capturar valores computados |
5. Loop for e Iteradores
(1) Loop for Básico
// Iteração de intervalo
for (i in 1..10) { /* i de 1 a 10 */ }
// Iteração de lista
for (order in orders) { println(order.id) }
// Com índice
for ((index, order) in orders.withIndex()) {
println("[$index] ${order.id}")
}
// Iteração de mapa
for ((id, status) in orderStatusMap) {
println("$id: $status")
}
(2) Protocolo de Iterador
// Qualquer classe com operator fun iterator() pode ser usada em loop for
class OrderBatch(val orders: List<Order>) {
operator fun iterator(): Iterator<Order> = orders.iterator()
}
val batch = OrderBatch(orders)
for (order in batch) { /* funciona! */ }
(3) Comparação de Padrões do Loop for
| Padrão | Sintaxe | Caso de Uso |
|---|---|---|
| Intervalo | for (i in 1..10) |
Loops de contagem |
| Coleção | for (o in orders) |
Iterar elementos |
| Com índice | for ((i, o) in ...) |
Precisa do índice |
| Mapa | for ((k,v) in map) |
Iteração chave-valor |
| Iterador personalizado | operator fun iterator() |
Contêineres personalizados |
6. Loop while
// while padrão
var retryCount = 0
while (retryCount < 3) {
val success = tryProcessOrder()
if (success) break
retryCount++
}
// do-while (executa pelo menos uma vez)
var input: String
do {
input = readOrderStatus()
} while (input !in listOf("CONFIRM", "CANCEL"))
7. Rótulos e Retornos
(1) Rótulos de Loop
// Rotular loop externo
loop@ for (batch in batches) {
for (order in batch) {
if (order.status == "CANCELLED") continue@loop // Pular para o próximo lote
if (order.total > 100_000) break@loop // Parar todo o processamento
process(order)
}
}
(2) Retornos de Lambda
// retorno de lambda (retorno local)
orders.forEach { order ->
if (order.status == "SKIP") return@forEach // Pular apenas este item
process(order)
}
// retorno da função envolvente (retorno não local)
// Funciona apenas em funções inline
orders.forEach { order ->
if (order.status == "FATAL") return // Sai do main()
process(order)
}
(3) Comparação de Tipos de Rótulos
| Tipo de Rótulo | Sintaxe | Efeito |
|---|---|---|
| Rótulo de loop | loop@ + break@loop |
Sair do loop especificado |
| Rótulo continue | continue@loop |
Ir para a próxima iteração do loop especificado |
| Retorno local de lambda | return@forEach |
Sair apenas do lambda atual |
| Retorno não local | return |
Sair da função envolvente (apenas inline) |
8. Exemplo Completo: Máquina de Estados de Pedidos do OrderProcessor
// ============================================
// OrderProcessor - Roteamento Baseado em Status
// Funcionalidade: Roteamento completo de status de pedido com when
// ============================================
enum class Status { PENDING, CONFIRMED, SHIPPED, DELIVERED, CANCELLED }
data class Order(val id: String, val total: Double, val status: Status, val region: String)
fun routeOrder(order: Order): String = when (order.status) {
Status.PENDING -> when {
order.region == "EU" -> "EU_COMPLIANCE_QUEUE"
order.total > 10_000 -> "HIGH_VALUE_QUEUE"
else -> "STANDARD_QUEUE"
}
Status.CONFIRMED -> when {
order.total > 5_000 -> "EXPRESS_SHIPPING"
else -> "REGULAR_SHIPPING"
}
Status.SHIPPED -> "TRACKING_UPDATE"
Status.DELIVERED -> "COMPLETION_QUEUE"
Status.CANCELLED -> when {
order.total > 1_000 -> "PRIORITY_REFUND"
else -> "STANDARD_REFUND"
}
}
fun processBatch(orders: List<Order>) {
loop@ for (order in orders) {
when (order.status) {
Status.CANCELLED -> {
println("PULAR: ${order.id} cancelado")
continue@loop
}
else -> {
val pipeline = routeOrder(order)
val priority = if (order.total > 10_000) "CRITICAL" else "NORMAL"
println("PROCESSAR: ${order.id} -> $pipeline [$priority]")
}
}
}
}
fun main() {
val orders = listOf(
Order("ORD-001", 299.99, Status.CONFIRMED, "US"),
Order("ORD-002", 15_000.00, Status.PENDING, "EU"),
Order("ORD-003", 2_500.00, Status.CONFIRMED, "US"),
Order("ORD-004", 45.50, Status.CANCELLED, "US"),
Order("ORD-005", 800.00, Status.PENDING, "ASIA"),
Order("ORD-006", 8_000.00, Status.SHIPPED, "US")
)
println("=== Roteamento de Pedidos ===")
processBatch(orders)
// Resumo usando resultados da expressão when
val summary = orders
.filter { it.status != Status.CANCELLED }
.groupBy { routeOrder(it) }
.mapValues { (pipeline, list) -> "${list.size} pedidos" }
println("\n=== Resumo por Pipeline ===")
summary.forEach { (pipeline, info) -> println(" $pipeline: $info") }
}
Saída:
=== Roteamento de Pedidos ===
PROCESSAR: ORD-001 -> REGULAR_SHIPPING [NORMAL]
PROCESSAR: ORD-002 -> EU_COMPLIANCE_QUEUE [CRITICAL]
PROCESSAR: ORD-003 -> EXPRESS_SHIPPING [NORMAL]
PULAR: ORD-004 cancelado
PROCESSAR: ORD-005 -> STANDARD_QUEUE [NORMAL]
PROCESSAR: ORD-006 -> TRACKING_UPDATE [NORMAL]
=== Resumo por Pipeline ===
REGULAR_SHIPPING: 1 pedidos
EU_COMPLIANCE_QUEUE: 1 pedidos
EXPRESS_SHIPPING: 1 pedidos
STANDARD_QUEUE: 1 pedidos
TRACKING_UPDATE: 1 pedidos
9. Exemplos práticos rápidos
▶ Exemplo: if como expressão
// if como expressão pode ser atribuído diretamente
val a = 10
val b = 20
val max = if (a > b) a else b
println("Max: $max")
// if multi-ramo como expressão
val score = 85
val grade = if (score >= 90) "A"
else if (score >= 80) "B"
else if (score >= 70) "C"
else "F"
println("Pontuação $score -> Nota $grade")
// Com blocos (última expressão é retornada)
val description = if (score >= 60) {
val bonus = score / 10
"Aprovado com bônus: $bonus"
} else {
"Reprovado"
}
println(description)
Saída:
Max: 20
Pontuação 85 -> Nota B
Aprovado com bônus: 8
▶ Exemplo: when como expressão com tipos
// when como expressão - força exaustividade
fun describe(value: Any): String = when (value) {
is String -> "Texto com comprimento ${value.length}: \"$value\""
is Int -> when {
value > 0 -> "Inteiro positivo"
value < 0 -> "Inteiro negativo"
else -> "Zero"
}
is List<*> -> "Lista com ${value.size} elementos"
is Map<*, *> -> "Mapa com ${value.size} entradas"
null -> "Valor nulo"
else -> "Outro tipo: ${value::class.simpleName}"
}
println(describe("Olá"))
println(describe(42))
println(describe(-5))
println(describe(listOf(1, 2, 3)))
println(describe(null))
println(describe(3.14))
Saída:
Texto com comprimento 3: "Olá"
Inteiro positivo
Inteiro negativo
Lista com 3 elementos
Valor nulo
Outro tipo: double
▶ Exemplo: when com intervalos e guardas
fun classifyOrder(total: Double, status: String): String = when {
status == "CANCELLED" -> "Cancelado - pular"
total > 10_000 -> "VIP - prioridade máxima"
total > 1_000 && status == "CONFIRMED" -> "Alta prioridade - envio rápido"
total in 500.0..1_000.0 -> "Médio - processamento padrão"
total < 0 -> "Erro - valor negativo"
else -> "Padrão"
}
println(classifyOrder(15_000.0, "CONFIRMED"))
println(classifyOrder(2_500.0, "CONFIRMED"))
println(classifyOrder(750.0, "PENDING"))
println(classifyOrder(-100.0, "NEW"))
println(classifyOrder(50.0, "CANCELLED"))
println(classifyOrder(50.0, "NEW"))
Saída:
VIP - prioridade máxima
Alta prioridade - envio rápido
Médio - processamento padrão
Erro - valor negativo
Cancelado - pular
Padrão
▶ Exemplo: for com intervalos e listas
// Intervalo fechado
print("1..5: ")
for (i in 1..5) print("$i ")
println()
// Intervalo decrescente com step
print("10 downTo 1 step 2: ")
for (i in 10 downTo 1 step 2) print("$i ")
println()
// Semiaberto (até mas não incluindo)
print("0 until 5: ")
for (i in 0 until 5) print("$i ")
println()
// Lista
val fruits = listOf("maçã", "banana", "laranja")
for (fruit in fruits) println("Fruta: $fruit")
// Com índice
for ((index, fruit) in fruits.withIndex()) {
println("$index: $fruit")
}
Saída:
1..5: 1 2 3 4 5
10 downTo 1 step 2: 10 8 6 4 2
0 until 5: 0 1 2 3 4
Fruta: maçã
Fruta: banana
Fruta: laranja
0: maçã
1: banana
2: laranja
▶ Exemplo: while e do-while
// while - verifica condição antes do loop
var countdown = 5
print("Contagem regressiva: ")
while (countdown > 0) {
print("$countdown ")
countdown--
}
println("Lançar!")
// do-while - executa pelo menos uma vez
var input = 0
var attempts = 0
do {
attempts++
input = (1..10).random()
println("Tentativa $attempts: obteve $input")
} while (input < 5 && attempts < 5)
println("Final: $input após $attempts tentativas")
Saída:
Contagem regressiva: 5 4 3 2 1 Lançar!
Tentativa 1: obteve 7
Final: 7 após 1 tentativas
▶ Exemplo: Rótulos em loops
// Procurando em uma matriz 2D
val matrix = arrayOf(
arrayOf(1, 2, 3),
arrayOf(4, 5, 6),
arrayOf(7, 8, 9)
)
var found = false
var targetValue = 5
outer@ for (i in matrix.indices) {
inner@ for (j in matrix[i].indices) {
if (matrix[i][j] == targetValue) {
println("Encontrado $targetValue em [$i][$j]")
found = true
break@outer // sai de ambos os loops
}
}
}
if (!found) println("$targetValue não encontrado")
// continue@label - pula para a próxima iteração de um loop específico
for (i in 1..5) {
if (i == 3) continue
print("$i ")
}
Saída:
Encontrado 5 em [1][1]
1 2 4 5
▶ Exemplo: return@label em lambdas
val orders = listOf(
"ORD-001" to "CONFIRMED",
"ORD-002" to "CANCELLED",
"ORD-003" to "PENDING",
"ORD-004" to "CONFIRMED"
)
// return@forEach pula apenas este elemento
orders.forEach { (id, status) ->
if (status == "CANCELLED") {
println("PULAR $id (cancelado)")
return@forEach
}
println("PROCESSAR $id ($status)")
}
// Procurando primeiro elemento correspondente
val firstCancelled = orders.firstOrNull { it.second == "CANCELLED" }
println("Primeiro cancelado: ${firstCancelled?.first}")
// takeWhile / dropWhile
val pendingOrders = orders.takeWhile { it.second == "CONFIRMED" }
println("Take while CONFIRMED: $pendingOrders")
val remaining = orders.dropWhile { it.second == "CONFIRMED" }
println("Drop while CONFIRMED: $remaining")
Saída:
PULAR ORD-002 (cancelado)
PROCESSAR ORD-001 (CONFIRMED)
PROCESSAR ORD-003 (PENDING)
PROCESSAR ORD-004 (CONFIRMED)
Primeiro cancelado: ORD-002
Take while CONFIRMED: [(ORD-001, CONFIRMED)]
Drop while CONFIRMED: [(ORD-002, CANCELLED), (ORD-003, PENDING), (ORD-004, CONFIRMED)]
❓ Perguntas Frequentes
P: O Kotlin tem um operador ternário
?:? R: Não. O Kotlin usaif/elseem vez de um operador ternário porqueifé uma expressão — funcionalmente equivalente e mais legível.
P: Qual a diferença entre
whencom e sem sujeito? R:when(x)corresponde ao valor/tipo de x com verificação de exaustividade pelo compilador;when { }são expressões condicionais arbitrárias e devem incluirelse.
P: Um loop for pode iterar sobre objetos personalizados? R: Sim, desde que a classe implemente
operator fun iterator()retornando umIterator.
P: Quais são as limitações do retorno não local? R: O retorno não local só pode ser usado em lambdas de funções
inline. Usar retorno não local em um lambda regular causa um erro de compilação.
P: A ordem dos ramos importa no
when? R: Sim.whencorresponde de cima para baixo, e o primeiro ramo satisfeito vence. Coloque condições mais específicas antes das mais gerais.
P: Como corresponder tanto um valor quanto uma condição no
when? R: Use condições de guarda:is Order && total > 1000 ->— primeiro corresponde ao tipo, depois adiciona a condição.
📖 Resumo
ifé uma expressão que pode ser diretamente atribuída a variáveis, substituindo o operador ternário do Javawhené a versão atualizada doswitch: correspondência de múltiplos valores, intervalos, tipos, condições de guarda- Quando
whené usado como expressão, o compilador impõe exaustividade para prevenir ramos ausentes forsuporta intervalos, coleções, mapas e iteradores personalizados- Rótulos com
break@+continue@controlam precisamente a navegação em loops aninhados - Retorno local de lambda
return@forEachvs retorno não localreturn
📝 Exercícios
- Iniciante (⭐): Use expressões
ifpara implementar uma funçãomaxOfThreeque retorna o máximo de três números. Dica: if aninhado ou usemaxOf()da biblioteca padrão - Intermediário (⭐⭐): Use
whenpara implementar determinação de prioridade de pedido: VIP (>10000 USD) > Alta Prioridade (>1000) > Normal, pule pedidos cancelados. Dica: condições de guardawhen - Desafio (⭐⭐⭐): Use rótulos de loop para implementar processamento em lote de pedidos: loop externo sobre lotes de clientes, loop interno sobre pedidos — pule todo o lote do cliente se um pedido cancelado for encontrado. Dica:
loop@+continue@loop