Neste tutorial, você vai aprender tudo sobre lambdas em Kotlin: desde a sintaxe básica até conceitos avançados como closures, function references e funções inline. Lambdas são um dos recursos mais poderosos da linguagem e estão presentes em praticamente todo código Kotlin moderno. Ao final deste guia, você vai dominar programação funcional em Kotlin e saberá usar as principais funções da standard library como let, run, with, apply e also.
O que São Lambdas?
Uma lambda é, essencialmente, uma função anônima — uma função sem nome que pode ser tratada como um valor. Em Kotlin, lambdas são cidadãs de primeira classe, o que significa que você pode armazená-las em variáveis, passá-las como argumento para outras funções e retorná-las de funções.
Se você já trabalhou com JavaScript, Python ou qualquer linguagem que suporte programação funcional, o conceito será familiar. A diferença é que Kotlin oferece uma sintaxe especialmente elegante para trabalhar com lambdas.
Sintaxe Básica de Lambdas
A sintaxe de uma lambda em Kotlin segue este formato:
val minhaLambda: (Int, Int) -> Int = { a, b -> a + b }
// Chamando a lambda
val resultado = minhaLambda(3, 5) // resultado = 8
println(resultado)
Observe os elementos: as chaves {} delimitam o corpo da lambda, os parâmetros ficam antes da seta ->, e o último valor da expressão é o retorno implícito. Não é necessário usar a palavra return dentro de uma lambda.
Quando a lambda tem apenas um parâmetro, Kotlin permite usar a palavra reservada it no lugar do nome do parâmetro:
val dobrar: (Int) -> Int = { it * 2 }
println(dobrar(7)) // 14
O uso de it é extremamente comum no dia a dia e torna o código mais conciso. No entanto, quando a lambda é complexa ou aninhada, é recomendável nomear o parâmetro explicitamente para manter a legibilidade.
Higher-Order Functions
Uma higher-order function é uma função que recebe outra função como parâmetro ou que retorna uma função. Em Kotlin, isso é natural graças ao suporte a lambdas.
fun operacao(a: Int, b: Int, op: (Int, Int) -> Int): Int {
return op(a, b)
}
// Usando com lambdas diferentes
val soma = operacao(10, 5) { a, b -> a + b } // 15
val subtracao = operacao(10, 5) { a, b -> a - b } // 5
val produto = operacao(10, 5) { a, b -> a * b } // 50
println("Soma: $soma, Subtração: $subtracao, Produto: $produto")
Note que quando a lambda é o último parâmetro de uma função, podemos movê-la para fora dos parênteses. Essa é a chamada trailing lambda syntax e é uma das convenções mais usadas em Kotlin.
Closures
Lambdas em Kotlin podem acessar e modificar variáveis do escopo externo — isso é o que chamamos de closure. Diferente de Java (onde variáveis capturadas precisam ser efetivamente final), em Kotlin a lambda pode alterar variáveis mutáveis do escopo:
fun contadorDeCliques(): () -> Int {
var cliques = 0
return {
cliques++
cliques
}
}
val contador = contadorDeCliques()
println(contador()) // 1
println(contador()) // 2
println(contador()) // 3
A lambda “captura” a variável cliques e mantém uma referência a ela mesmo após a função contadorDeCliques ter retornado. Closures são muito úteis para criar funções com estado interno.
Function References (::)
Além de lambdas, Kotlin permite passar referências a funções já existentes usando o operador ::. Isso é chamado de function reference:
fun ehPar(numero: Int): Boolean = numero % 2 == 0
val numeros = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
// Usando function reference em vez de lambda
val pares = numeros.filter(::ehPar)
println(pares) // [2, 4, 6, 8, 10]
// Equivalente com lambda:
val paresLambda = numeros.filter { it % 2 == 0 }
Você também pode referenciar métodos de uma classe ou construtores:
data class Usuario(val nome: String, val idade: Int)
val nomes = listOf("Ana", "Bruno", "Carla")
// Referência ao construtor
val usuarios = nomes.map { Usuario(it, 25) }
// Referência a método de instância
val tamanhos = nomes.map(String::length)
println(tamanhos) // [3, 5, 5]
Funções Inline
Quando usamos higher-order functions com lambdas, cada lambda cria um objeto em memória. Para evitar esse overhead, Kotlin oferece a palavra-chave inline:
inline fun medirTempo(bloco: () -> Unit) {
val inicio = System.currentTimeMillis()
bloco()
val fim = System.currentTimeMillis()
println("Executado em ${fim - inicio}ms")
}
medirTempo {
// Código a ser medido
val lista = (1..1_000_000).toList().shuffled().sorted()
println("Lista ordenada com ${lista.size} elementos")
}
Funções inline substituem a chamada da lambda pelo seu corpo diretamente no local de chamada durante a compilação. Isso elimina a criação de objetos e melhora a performance, especialmente em loops e código crítico.
Lambdas da Standard Library
Kotlin oferece um conjunto de funções de escopo extremamente úteis que usam lambdas. Vamos explorar cada uma delas:
let
Usada para executar um bloco de código com um objeto como argumento. Muito comum para null checks:
val nome: String? = "Kotlin Brasil"
nome?.let {
println("O nome tem ${it.length} caracteres")
println("Em maiúsculas: ${it.uppercase()}")
}
// Se nome for null, o bloco não é executado
run
Similar ao let, mas o objeto é acessado via this em vez de it:
val resultado = "Kotlin".run {
println("O texto é: $this")
length // retorno implícito
}
println("Tamanho: $resultado") // 6
with
Igual ao run, mas o objeto é passado como argumento em vez de ser o receiver:
val builder = StringBuilder()
with(builder) {
append("Kotlin ")
append("é ")
append("incrível!")
}
println(builder.toString()) // Kotlin é incrível!
apply
Configura um objeto e retorna o próprio objeto. Ideal para inicialização:
val usuario = Usuario("Ana", 28).apply {
// Imagine que Usuario é uma classe com propriedades mutáveis
println("Criando usuário: $nome")
}
also
Executa uma ação adicional e retorna o objeto original. Útil para logging e debugging:
val numeros = mutableListOf(1, 2, 3)
.also { println("Lista original: $it") }
.apply { add(4) }
.also { println("Após adicionar: $it") }
Exemplo Prático Completo
Vamos combinar tudo em um exemplo realista — um mini sistema de filtro de produtos:
data class Produto(val nome: String, val preco: Double, val categoria: String)
fun List<Produto>.filtrarE(
vararg filtros: (Produto) -> Boolean
): List<Produto> {
return this.filter { produto ->
filtros.all { filtro -> filtro(produto) }
}
}
fun main() {
val produtos = listOf(
Produto("Notebook", 3500.0, "Eletrônicos"),
Produto("Camiseta", 79.90, "Roupas"),
Produto("Smartphone", 2200.0, "Eletrônicos"),
Produto("Calça Jeans", 150.0, "Roupas"),
Produto("Fone Bluetooth", 199.90, "Eletrônicos")
)
val resultado = produtos
.filtrarE(
{ it.categoria == "Eletrônicos" },
{ it.preco < 3000.0 }
)
.sortedBy { it.preco }
.also { println("Encontrados ${it.size} produtos") }
resultado.forEach { produto ->
println("${produto.nome} — R$ ${"%.2f".format(produto.preco)}")
}
}
Erros Comuns
Confundir
item lambdas aninhadas: quando você tem lambdas dentro de lambdas, cada uma tem seu próprioit. Nomeie os parâmetros explicitamente para evitar ambiguidade.Esquecer que o último valor é o retorno: em Kotlin, a última expressão de uma lambda é o retorno implícito. Não use
returndentro de lambdas a menos que queira fazer um non-local return (que retorna da função externa).Usar
inlinedesnecessariamente: marcar funções comoinlineé útil apenas quando elas recebem lambdas como parâmetro. Usarinlineem funções comuns pode aumentar o tamanho do bytecode sem benefício.Não entender a diferença entre scope functions:
letusait,run/with/applyusamthis, eapply/alsoretornam o objeto original enquantolet/run/withretornam o resultado da lambda.Capturar variáveis mutáveis em closures sem cuidado: embora Kotlin permita, alterar variáveis externas dentro de lambdas pode gerar bugs difíceis de rastrear, especialmente em código assíncrono com coroutines.
Conclusão e Próximos Passos
Lambdas são o coração da programação funcional em Kotlin. Neste tutorial, você aprendeu a sintaxe básica, como usar it, closures, function references com ::, funções inline e as cinco scope functions da standard library. Com esse conhecimento, você está preparado para escrever código Kotlin idiomático e conciso.
Para continuar sua jornada, recomendamos explorar os seguintes tópicos:
- Extension Functions para criar funções mais expressivas
- Generics para trabalhar com tipos parametrizados em lambdas
- Collections e suas operações funcionais avançadas como
fold,reduceegroupBy - Coroutines para usar lambdas em programação assíncrona
Pratique criando suas próprias higher-order functions e experimentando as scope functions no seu código diário. Quanto mais você usar lambdas, mais natural a sintaxe vai se tornar.