Kotlin: Funções e Expressões Lambda do Kotlin

Última atualização: 2026-08-26

Funções são cidadãos de primeira classe no Kotlin — funções de ordem superior + lambdas + inline permitem que Charlie construa os pipelines de processamento de dados do OrderProcessor em estilo funcional, reduzindo o código em 60%.

1. O que Você Aprenderá


2. A História Real de um Desenvolvedor

(1) Problema: Lógica Duplicada de Processamento de Pedidos

Na equipe do Charlie, cada desenvolvedor escrevia sua própria lógica de filtragem/transformação/agregação de pedidos — 100+ linhas de código duplicado espalhadas por 5 serviços. Mudar uma condição de filtro significava alterar em 5 lugares.

(2) Funções de Ordem Superior Eliminam Duplicação

KOTLIN
// Processador de pedidos genérico - uma função substitui 5 cópias
fun <T> processOrders(
    orders: List<Order>,
    filter: (Order) -> Boolean,
    transform: (Order) -> T
): List<T> = orders.filter(filter).map(transform)

// Uso: cada chamada são 2 linhas em vez de 20
val highValueIds = processOrders(orders, { it.total > 1_000 }, { it.id })
val vipCustomers = processOrders(orders, { it.total > 10_000 }, { it.customer })

Funções de ordem superior parametrizam "o que muda" para que "o que permanece" seja escrito apenas uma vez.


3. Declarações de Funções

(1) Funções Básicas

KOTLIN
// Corpo de bloco
fun calculateTax(total: Double, rate: Double = 0.08): Double {
    return total * rate
}

// Corpo de expressão
fun calculateTax(total: Double, rate: Double = 0.08) = total * rate

// Retorno Unit (void)
fun logOrder(order: Order) {
    println("Processando ${order.id}")
}

(2) Parâmetros Padrão e Nomeados

KOTLIN
// Parâmetros padrão - elimina sobrecarga de métodos
fun createOrder(
    id: String,
    total: Double,
    status: String = "PENDING",
    currency: String = "USD",
    taxRate: Double = 0.08
): Order = Order(id, total, status, currency)

// Argumentos nomeados - chame qualquer parâmetro pelo nome
val order1 = createOrder("ORD-001", 299.99)
val order2 = createOrder("ORD-002", 1_500.00, taxRate = 0.10)
val order3 = createOrder("ORD-003", 45.50, currency = "EUR", status = "CONFIRMED")

(3) Parâmetros Padrão vs Sobrecarga de Métodos Java

Dimensão Sobrecarga Java Parâmetros Padrão Kotlin
Tamanho do código N métodos 1 método
Manutenibilidade Mudar N lugares para um parâmetro Mudar um lugar
Flexibilidade de chamada Apenas ordem posicional Argumentos nomeados, qualquer combinação
Interoperabilidade Java chama diretamente Precisa de @JvmOverloads

4. Funções de Ordem Superior

Funções de ordem superior recebem funções como parâmetros ou retornam funções — a pedra angular da programação funcional do Kotlin.

(1) Tipos de Função

KOTLIN
// Sintaxe de tipo de função
val transform: (Order) -> String = { order -> order.id }
val predicate: (Order) -> Boolean = { it.total > 1_000 }
val aggregator: (Double, Order) -> Double = { acc, order -> acc + order.total }
val supplier: () -> Order = { Order("DEFAULT", 0.0, "PENDING") }

// Função como parâmetro
fun processOrders(orders: List<Order>, transform: (Order) -> String): List<String> {
    return orders.map(transform)
}

(2) Função como Valor de Retorno

KOTLIN
// Retornar uma função
fun getDiscountStrategy(tier: String): (Double) -> Double = when (tier) {
    "VIP" -> { total -> total * 0.80 }       // 20% de desconto
    "GOLD" -> { total -> total * 0.90 }      // 10% de desconto
    else -> { total -> total }               // Sem desconto
}

val discount = getDiscountStrategy("VIP")
val finalPrice = discount(1_000.00)  // 800.0

(3) Função de Ordem Superior + Cadeia de Chamadas Lambda

100%
flowchart LR
    A[Pedidos] --> B[filter]
    B --> C[map]
    C --> D[sortedBy]
    D --> E[fold]
    E --> F[Resultado]

5. Expressões Lambda

(1) Sintaxe Lambda

KOTLIN
// Sintaxe completa
val total = orders.map({ order: Order -> order.total })

// Inferência de tipo
val total = orders.map({ order -> order.total })

// Palavra-chave it para parâmetro único
val total = orders.map({ it.total })

// Convenção de lambda final
val total = orders.map { it.total }

// Lambda com múltiplos parâmetros
val result = orders.fold(0.0) { acc, order -> acc + order.total }

(2) Níveis de Simplificação da Sintaxe Lambda

Nível Sintaxe Descrição
Completa { order: Order -> order.total } Tipo explícito
Simplificada 1 { order -> order.total } Inferência de tipo
Simplificada 2 { it.total } Parâmetro único com it
Lambda final orders.map { it.total } Lambda fora dos parênteses

(3) Padrões Lambda Comuns

KOTLIN
// let: transformar e consumir
val formatted = order.total.let { "\$$it USD" }

// also: efeito colateral sem alterar o valor
val order = createOrder("ORD-001", 299.99).also {
    println("Pedido criado: ${it.id}")
}

// apply: configurar objeto
val order = Order("ORD-001", 0.0, "PENDING").apply {
    // Só pode ser usado com propriedades var
}

// run: executar bloco e retornar resultado
val summary = order.run {
    "Pedido $id: \$${total} USD ($status)"
}

6. Convenção de Lambda Final

Quando o último parâmetro de uma função é um lambda, ele pode ser escrito fora dos parênteses — este é o açúcar sintático mais importante do Kotlin.

(1) Uso Básico

KOTLIN
// Definição
fun processOrders(orders: List<Order>, callback: (Order) -> Unit) { ... }

// Chamada: lambda final
processOrders(orders) { order ->
    println(order.id)
}

// Se o lambda é o ÚNICO parâmetro, omita os parênteses
orders.forEach { println(it.id) }

(2) Lambda Final como Base de DSLs

KOTLIN
// É assim que DSLs funcionam
fun order(id: String, init: OrderBuilder.() -> Unit): Order {
    val builder = OrderBuilder(id)
    builder.init()
    return builder.build()
}

// Uso: lambda final faz ler como configuração
val myOrder = order("ORD-001") {
    total(299.99)
    status("CONFIRMED")
    customer("Alice")
}

7. Funções Inline

Lambdas compilam para objetos de classe anônima na JVM, criando novas instâncias a cada chamada. inline elimina esse overhead.

(1) Básico do inline

KOTLIN
// Sem inline: Lambda cria um objeto a cada chamada
fun process(order: Order, block: (Order) -> String): String {
    return block(order)
}

// Com inline: código do lambda é inserido no local da chamada
inline fun process(order: Order, block: (Order) -> String): String {
    return block(order)
}

(2) Comparação do Efeito do inline

KOTLIN
// Após a inlining, isso:
process(order) { it.id }

// Torna-se (conceitualmente):
run { order.id }

(3) inline vs noinline vs crossinline

Modificador Efeito Caso de Uso
inline Código do lambda inserido no local da chamada Lambdas chamados frequentemente
noinline Impede inlining (permanece como objeto) Lambda precisa ser armazenado/passado
crossinline Inserido, mas proíbe retorno não local Lambda em um contexto de execução diferente

(4) Parâmetros de Tipo reified

KOTLIN
// Normal: tipo é apagado em tempo de execução
fun <T> isType(value: Any): Boolean = value is T  // ERRO

// reified: inline preserva informação de tipo
inline fun <reified T> isType(value: Any): Boolean = value is T  // OK

isType<String>("Hello")  // true
isType<Int>(42)          // true
isType<Int>("42")        // false

8. Exemplo Completo: Pipeline Funcional do OrderProcessor

KOTLIN
// ============================================
// OrderProcessor - Pipeline Funcional
// Funcionalidade: Processar pedidos com funções de ordem superior
// ============================================

data class Order(val id: String, val total: Double, val status: String, val customer: String)

// Processador de pipeline genérico
inline fun <T, R> List<T>.pipeline(
    filter: (T) -> Boolean,
    transform: (T) -> R,
    crossinline postProcess: (R) -> Unit
): List<R> {
    return this.filter(filter).map(transform).also { results ->
        results.forEach(postProcess)
    }
}

// Fábrica de estratégia de desconto
fun getDiscountStrategy(tier: String): (Double) -> Double = when (tier) {
    "VIP" -> { total -> total * 0.80 }
    "GOLD" -> { total -> total * 0.90 }
    "SILVER" -> { total -> total * 0.95 }
    else -> { total -> total }
}

fun main() {
    val orders = listOf(
        Order("ORD-001", 299.99, "CONFIRMED", "Alice"),
        Order("ORD-002", 15_000.00, "CONFIRMED", "Bob"),
        Order("ORD-003", 2_500.00, "PENDING", "Charlie"),
        Order("ORD-004", 45.50, "CANCELLED", "Alice"),
        Order("ORD-005", 8_000.00, "CONFIRMED", "Bob")
    )

    // Pipeline: filtrar confirmados -> aplicar desconto VIP -> registrar
    val discount = getDiscountStrategy("VIP")
    val processed = orders.pipeline(
        filter = { it.status == "CONFIRMED" },
        transform = { it.copy(total = discount(it.total)) },
        postProcess = { println("  Processado: ${it.id} -> \$${it.total} USD") }
    )

    // Agregar resultados
    val totalRevenue = processed.sumOf { it.total }
    val avgOrder = processed.map { it.total }.average()
    println("\nReceita: \$$totalRevenue USD | Média: \$${"%.2f".format(avgOrder)} USD")

    // Composição funcional com let/also/run
    val report = orders
        .filter { it.status != "CANCELLED" }
        .groupBy { it.customer }
        .mapValues { (_, list) ->
            list.sumOf { it.total }
        }
        .also { println("\nReceita por cliente:") }
        .map { (customer, total) -> "$customer: \$$total USD" }
        .joinToString("\n  ")

    println("  $report")
}

Saída:

TEXT 📖 Somente leitura
  Processado: ORD-001 -> $239.992 USD
  Processado: ORD-002 -> $12000.0 USD
  Processado: ORD-005 -> $6400.0 USD

Receita: $18639.992 USD | Média: $6213.330666666667 USD

Receita por cliente:
  Alice: $299.99 USD
  Bob: $23000.0 USD
  Charlie: $2500.0 USD

9. Exemplos práticos rápidos

▶ Exemplo: Parâmetros padrão e nomeados

KOTLIN
// Parâmetros com valor padrão - reduz sobrecargas
fun createOrder(
    id: String,
    total: Double,
    currency: String = "USD",
    status: String = "PENDING",
    express: Boolean = false
): String {
    val shipping = if (express) "EXPRESS" else "STANDARD"
    return "$id | $total $currency | $status | $shipping"
}

// Chamada posicional
println(createOrder("ORD-001", 299.99))

// Chamada com argumentos nomeados - legibilidade máxima
println(createOrder(
    id = "ORD-002",
    total = 1500.0,
    express = true,
    status = "CONFIRMED"
))

// Argumentos nomeados em qualquer ordem
println(createOrder(
    total = 50.0,
    id = "ORD-003",
    currency = "EUR"
))

Saída:

TEXT 📖 Somente leitura
ORD-001 | 299.99 USD | PENDING | STANDARD
ORD-002 | 1500.0 USD | CONFIRMED | EXPRESS
ORD-003 | 50.0 EUR | PENDING | STANDARD

▶ Exemplo: Funções de ordem superior

KOTLIN
// Função que recebe outra função como parâmetro
fun calculateTotal(
    items: List<Double>,
    discount: (Double) -> Double
): Double {
    val subtotal = items.sum()
    return discount(subtotal)
}

// Lambdas para diferentes políticas
val vipDiscount = { total: Double -> if (total > 10_000) total * 0.8 else total }
val seasonalDiscount = { total: Double -> total * 0.9 }
val noDiscount = { total: Double -> total }

val order1 = calculateTotal(listOf(5000.0, 4000.0, 3000.0), vipDiscount)
val order2 = calculateTotal(listOf(500.0, 300.0), seasonalDiscount)
val order3 = calculateTotal(listOf(100.0), noDiscount)

println("VIP: \$$order1")        // 9600.0 (20% off)
println("Sazonal: \$$order2")    // 720.0 (10% off)
println("Normal: \$$order3")     // 100.0

Saída:

TEXT 📖 Somente leitura
VIP: $9600.0
Sazonal: $720.0
Normal: $100.0

▶ Exemplo: Lambdas com it e closures

KOTLIN
// Lambda simples com `it`
val names = listOf("Alice", "Bob", "Charlie")
val greetings = names.map { "Olá, $it!" }
println(greetings)

// Lambda com captura (closure)
fun makeCounter(start: Int = 0): () -> Int {
    var count = start
    return { count++ }
}

val counter1 = makeCounter()
val counter2 = makeCounter(100)

println("counter1: ${counter1()}, ${counter1()}, ${counter1()}")
println("counter2: ${counter2()}, ${counter2()}")

// Modificando variáveis externas
var total = 0
val orders = listOf(100.0, 200.0, 300.0)
orders.forEach { total += it.toInt() }
println("Total: $total")

Saída:

TEXT 📖 Somente leitura
[Olá, Alice!, Olá, Bob!, Olá, Charlie!]
counter1: 0, 1, 2
counter2: 100, 101
Total: 600

▶ Exemplo: Funções inline e reified

KOTLIN
// inline: evitar overhead de lambda em loops
inline fun <T> List<T>.customEach(action: (T) -> Unit) {
    for (item in this) action(item)
}

// reified: tipo disponível em tempo de execução (apenas em funções inline)
inline fun <reified T> List<*>.filterByType(): List<T> = filterIsInstance<T>()

val mixed = listOf("Alice", 42, "Bob", 3.14, "Charlie")
val strings = mixed.filterByType<String>()
val ints = mixed.filterByType<Int>()

println("Strings: $strings")
println("Ints: $ints")

// Uso de customEach
mixed.customEach { println("Item: $it") }

Saída:

TEXT 📖 Somente leitura
Strings: [Alice, Bob, Charlie]
Ints: [42]
Item: Alice
Item: 42
Item: Bob
Item: 3.14
Item: Charlie

▶ Exemplo: Operador infix

KOTLIN
// Operador infix: método de um parâmetro que pode ser chamado sem ponto/parênteses
infix fun String.times(count: Int): String = this.repeat(count)
infix fun Int.to(range: IntRange): List<Int> = (this..range).toList()

// Uso sem pontos
val shout = "Olá " times 3
println(shout)

val range = 1 to 10
println("Intervalo: $range")

// Composição com chamadas normais
val frames = 60.times(2)
println("Quadros: $frames")

Saída:

TEXT 📖 Somente leitura
Olá Olá Olá 
Intervalo: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Quadros: 120

▶ Exemplo: Funções de escopo

KOTLIN
// let: transformação segura, execução de lambda no objeto
val order: String? = "ORD-001"
val length = order?.let {
    println("Pedido: $it")
    it.length
}
println("Comprimento: $length")

// also: efeitos colaterais, retorna o próprio objeto
val processed = "ORD-002".also {
    println("Log: processando $it")
}.also {
    println("Notificando $it")
}
println("Resultado: $processed")

// with: contexto múltiplo para configuração
val config = with(mutableMapOf<String, Any>()) {
    put("env", "produção")
    put("debug", false)
    put("timeout", 30)
    this
}
println("Config: $config")

Saída:

TEXT 📖 Somente leitura
Pedido: ORD-001
Comprimento: 7
Log: processando ORD-002
Notificando ORD-002
Resultado: ORD-002
Config: {env=produção, debug=false, timeout=30}

▶ Exemplo: Recursão e tailrec

KOTLIN
// Função recursiva padrão - pode causar StackOverflow
fun factorial(n: Int): Long {
    return if (n <= 1) 1 else n * factorial(n - 1)
}
println("5! = ${factorial(5)}")

// tailrec: otimiza recursão em cauda para loop
tailrec fun factorialTailrec(n: Int, acc: Long = 1): Long {
    return if (n <= 1) acc else factorialTailrec(n - 1, n * acc)
}
println("10! = ${factorialTailrec(10)}")

// Fibonacci otimizado
tailrec fun fib(n: Int, a: Long = 0, b: Long = 1): Long {
    return if (n == 0) a else fib(n - 1, b, a + b)
}
println("Fib(20) = ${fib(20)}")

Saída:

TEXT 📖 Somente leitura
5! = 120
10! = 3628800
Fib(20) = 6765

❓ Perguntas Frequentes

P: Qual a diferença entre um lambda e uma função anônima? R: Lambdas usam it como parâmetro implícito e return se comporta de forma diferente (lambda return é não local, função anônima return é local). Use lambdas na maioria dos casos.

P: Quando devo usar it vs nome explícito? R: Use it quando o corpo do lambda tem 1-2 linhas e o contexto é claro; use nomes explícitos para corpos mais longos ou lambdas aninhados para melhorar a legibilidade.

P: Todas as funções de ordem superior devem ser inline? R: Não. inline é melhor para corpos de função pequenos onde o lambda é chamado diretamente. Inlining funções grandes aumenta o tamanho do código. A maioria das funções de ordem superior da biblioteca padrão são inline.

P: reified pode ser usado em classes regulares? R: Não. reified só funciona em parâmetros de tipo de funções inline, porque inlining permite ao compilador preservar informação de tipo no local da chamada. Parâmetros de tipo de funções regulares são apagados em tempo de execução.

P: A convenção de lambda final é apenas açúcar sintático? R: É açúcar sintático, mas criticamente importante — é a base da construção de DSLs Kotlin. Gradle Kotlin DSL, Compose UI, Anko, etc. todos dependem desta convenção.

P: Como escolher entre let/also/apply/run? R: let para transformar valores, also para efeitos colaterais, apply para configurar objetos (retorna this), run para executar um bloco (retorna o resultado). Lembre-se: apply retorna this, run retorna o resultado do lambda.


📖 Resumo


📝 Exercícios

  1. Iniciante (⭐): Escreva uma função calculateTotal com parâmetros padrão e chame-a com argumentos nomeados. Dica: fun calc(price: Double, tax: Double = 0.08)
  2. Intermediário (⭐⭐): Implemente processAndTransform com funções de ordem superior, aceitando uma condição de filtro e uma função de transformação, retornando uma lista de resultados. Dica: <T> fun List<T>.process...
  3. Desafio (⭐⭐⭐): Implemente uma função filterByType<Any> que filtra elementos de um tipo especificado de uma lista de tipos mistos. Dica: inline fun <reified T> List<Any>.filterByType(): List<T>

← Anterior | Próximo →

Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%