O que é Lambda em Kotlin?

Resposta rápida: uma lambda em Kotlin é uma função sem nome declarada entre chaves, com os parâmetros antes do ->: { x: Int -> x * 2 }. Ela é tratada como valor — dá para guardar em variáveis, passar como argumento (é a base de filter, map e forEach) e retornar de outras funções. Quando tem um único parâmetro, você pode referenciá-lo como it sem declarar nada.

Uma lambda é uma função anônima — uma função sem nome que pode ser tratada como valor. Você pode armazená-la em variáveis, passá-la como argumento ou retorná-la de outra função. Lambdas são a base da programação funcional em Kotlin.

A sintaxe é simples: tudo fica entre chaves {}, com os parâmetros antes da seta -> é o corpo depois.

Sintaxe básica

val saudacao = { nome: String -> "Olá, $nome!" }

fun main() {
    println(saudacao("Karina"))  // Olá, Karina!
}

Lambda como argumento

O uso mais comum de lambdas é como argumento de funções, especialmente com coleções:

fun main() {
    val numeros = listOf(1, 2, 3, 4, 5, 6)

    val pares = numeros.filter { it % 2 == 0 }
    println(pares) // [2, 4, 6]

    val dobrados = numeros.map { it * 2 }
    println(dobrados) // [2, 4, 6, 8, 10, 12]
}

Quando a lambda tem um único parâmetro, você pode usar it em vez de declarar o nome.

Trailing lambda

Se o último parâmetro de uma função é uma lambda, você pode escrevê-la fora dos parênteses:

val nomes = listOf("Ana", "Bruno", "Carla")

// Essas duas formas são equivalentes:
nomes.forEach({ println(it) })
nomes.forEach { println(it) }  // Mais idiomático

Essa convenção deixa o código mais limpo e é usada em toda parte no Kotlin.

Lambda com múltiplos parâmetros

val soma = { a: Int, b: Int -> a + b }
println(soma(3, 7)) // 10

val nomes = mapOf("BR" to "Brasil", "PT" to "Portugal")
nomes.forEach { (codigo, pais) ->
    println("$codigo -> $pais")
}

Lambda com receiver

Kotlin suporta lambdas com receiver, que são a base das DSLs:

fun construirTexto(bloco: StringBuilder.() -> Unit): String {
    val sb = StringBuilder()
    sb.bloco()
    return sb.toString()
}

fun main() {
    val texto = construirTexto {
        append("Kotlin ")
        append("Brasil!")
    }
    println(texto) // Kotlin Brasil!
}

Captura de variáveis (closure)

Lambdas em Kotlin podem acessar e modificar variáveis do escopo externo, formando o que se chama de closure:

fun criarContador(): () -> Int {
    var contagem = 0
    return {
        contagem++
        contagem
    }
}

fun main() {
    val contador = criarContador()
    println(contador()) // 1
    println(contador()) // 2
    println(contador()) // 3
}

Diferente de Java, onde variáveis capturadas devem ser efetivamente finais, Kotlin permite que lambdas modifiquem variáveis var do escopo externo. Isso é possível porque o compilador encapsula a variável em um objeto wrapper.

Lambda vs função anônima

Kotlin também oferece funções anônimas, que são semelhantes a lambdas mas com sintaxe diferente e comportamento de return distinto:

fun main() {
    val numeros = listOf(1, 2, 3, 4, 5)

    // Lambda: "return" faz non-local return (se inline)
    numeros.forEach {
        if (it == 3) return@forEach // Label necessario para sair só do lambda
        println(it)
    }

    // Função anônima: "return" retorna da funcao anônima
    numeros.forEach(fun(numero) {
        if (numero == 3) return // Retorna apenas desta funcao anônima
        println(numero)
    })
}

A principal diferença prática: em lambdas, return sem label retorna da função envolvente (se inline). Em funções anônimas, return sempre retorna da própria função anônima.

Lambda x função anônima x função nomeada

SituaçãoMelhor escolhaPor quê
Callback curto de 1 a 3 linhas (filter, map, setOnClickListener)LambdaSintaxe mínima e idiomática
Precisa retornar antes do fim do blocoFunção anônimareturn sai só da função, sem label
Lógica reutilizada em vários lugaresFunção nomeada + :: referênciaEvita duplicação e facilita testes
Configuração fluente de objetos e DSLsLambda com receiverapply { }, buildList { } e afins
Corpo com mais de 5–7 linhasFunção nomeadaExtraia e passe como ::funcao

Regra prática: comece com uma lambda; extraia para função nomeada quando ela crescer ou quando precisar de return controlado.

SAM conversion

Kotlin converte lambdas automaticamente para interfaces Java com um único método abstrato (SAM - Single Abstract Method):

// Interface Java funcional
// public interface OnClickListener { void onClick(View view); }

// Em vez de criar uma classe anônima:
button.setOnClickListener { view ->
    println("Botão clicado!")
}

Para interfaces Kotlin, a conversão SAM funciona quando a interface é declarada com fun interface:

fun interface Transformador<T> {
    fun transformar(valor: T): T
}

fun aplicar(valor: Int, transformador: Transformador<Int>): Int {
    return transformador.transformar(valor)
}

fun main() {
    // SAM conversion: lambda no lugar da interface
    val resultado = aplicar(5) { it * 3 }
    println(resultado) // 15
}

Casos de Uso no Mundo Real

  • Processamento de coleções: lambdas são usadas em praticamente toda operação com listas, maps e sets no Kotlin — filter, map, reduce, groupBy, sortedBy e dezenas de outros operadores recebem lambdas.
  • Callbacks em aplicações Android: listeners de clique, observadores de LiveData e callbacks de animação são substituídos por lambdas concisas no lugar de classes anônimas verbosas do Java.
  • Configuração de objetos com scope functions: apply { }, also { } e let { } usam lambdas para configurar objetos de forma fluente e legível.
  • DSLs type-safe: lambdas com receiver permitem criar APIs como o HTML DSL do Kotlin (html { body { p { +"texto" } } }) e as definições de rotas do Ktor.

Boas Práticas

  • Use it apenas quando a lambda é curta e o significado do parâmetro é óbvio. Em lambdas mais longas, dê um nome descritivo ao parâmetro.
  • Prefira trailing lambda syntax quando a lambda é o último ou único argumento. Isso é o padrão idiomático em Kotlin.
  • Evite lambdas com mais de 5-7 linhas. Se a lógica é complexa, extraia para uma função nomeada e passe como referência com ::.
  • Use destructuring declarations em lambdas quando trabalhar com pares ou data classes: map.forEach { (chave, valor) -> ... }.
  • Prefira fun interface em Kotlin quando precisar de SAM conversion para interfaces Kotlin, em vez de usar tipos de função diretamente.

Erros Comuns

  • Confundir return em lambda e em função anônima: em lambdas inline, return sai da função envolvente. Use return@nomeDaFuncao para retornar apenas da lambda. Em funções anônimas, return sempre retorna da função anônima.
  • Usar it em lambdas aninhadas: quando há lambdas dentro de lambdas, it se refere ao parâmetro da lambda mais interna. Isso causa confusão. Nomeie os parâmetros explicitamente.
  • Capturar variáveis mutáveis inadvertidamente: lambdas que capturam variáveis var podem gerar bugs quando executadas de forma assíncrona, pois o valor da variável pode mudar entre a criação da lambda e sua execução.
  • Esquecer que lambdas criam objetos: cada lambda não-inline cria um objeto na JVM. Em loops de alta frequência, isso pode causar pressão no garbage collector. Use inline na função receptora para evitar.
  • Não aproveitar a desestruturação: escrever { par -> par.first } em vez de { (primeiro, _) -> primeiro } torna o código menos legível quando se trabalha com pares ou data classes.

Perguntas Frequentes

Qual a diferença entre lambda e higher-order function? Uma lambda é uma função anônima que pode ser tratada como valor. Uma higher-order function é uma função que recebe ou retorna outras funções (incluindo lambdas). São conceitos complementares.

Lambdas afetam a performance? Sem o modificador inline na função receptora, cada lambda cria um objeto Function na JVM. Para a maioria dos casos, o impacto é desprezível. Em hot paths, use funções inline para eliminar essa alocação.

Posso usar lambdas com coroutines? Sim. Lambdas suspensas (do tipo suspend () -> T) são usadas extensivamente em coroutines. Funções como launch, async e withContext recebem lambdas suspensas como parâmetro.

Como declarar o tipo de uma lambda explicitamente? Use a sintaxe de tipo funcional (parametros) -> retorno. Por exemplo: var operacao: (Int, Int) -> Int = { a, b -> a + b }. Isso é útil em propriedades de classe, parâmetros de API pública e quando a inferência não é suficiente. Uma lambda suspenda usa suspend () -> Unit como tipo.

Quando devo usar função anônima em vez de lambda? Use função anônima quando precisar de um return que retorne apenas da própria função e não da função envolvente, ou quando quiser declarar o tipo de retorno explicitamente. Na maioria dos casos, lambdas são a escolha preferida.

Próximos Passos

Termos Relacionados

  • Higher-Order Function — funções que recebem ou retornam lambdas
  • Fun — palavra-chave para declarar funções nomeadas, em contraste com lambdas (anônimas)
  • Inline — modificador que elimina o overhead de alocação de objetos para lambdas
  • Flow — API de streams assíncronos que utiliza lambdas em seus operadores

Lambdas estão por toda parte no Kotlin. Dominar essa sintaxe é essencial pra escrever código idiomático e produtivo.