O que é Higher-Order Function em Kotlin?
Uma Higher-Order Function (função de ordem superior) é uma função que recebe outra função como parâmetro ou retorna uma função. Em Kotlin, funções são cidadãs de primeira classe, então esse tipo de construção é super natural.
Você já usa higher-order functions sem perceber: map, filter, forEach — todas elas recebem lambdas como argumento.
Recebendo funções como parâmetro
fun executarOperacao(a: Int, b: Int, operacao: (Int, Int) -> Int): Int {
return operacao(a, b)
}
fun main() {
val soma = executarOperacao(10, 5) { a, b -> a + b }
val produto = executarOperacao(10, 5) { a, b -> a * b }
println("Soma: $soma") // Soma: 15
println("Produto: $produto") // Produto: 50
}
O tipo (Int, Int) -> Int declara que o parâmetro é uma função que recebe dois inteiros e retorna um inteiro.
Retornando funções
fun criarMultiplicador(fator: Int): (Int) -> Int {
return { numero -> numero * fator }
}
fun main() {
val dobrar = criarMultiplicador(2)
val triplicar = criarMultiplicador(3)
println(dobrar(5)) // 10
println(triplicar(5)) // 15
}
Aqui, criarMultiplicador retorna uma função. Cada vez que você chama, recebe uma função nova configurada com o fator escolhido.
Exemplo prático: válidação
fun validar(valor: String, vararg regras: (String) -> Boolean): Boolean {
return regras.all { regra -> regra(valor) }
}
fun main() {
val naoVazio: (String) -> Boolean = { it.isNotBlank() }
val tamanhoMinimo: (String) -> Boolean = { it.length >= 3 }
val semEspaco: (String) -> Boolean = { !it.contains(" ") }
println(validar("kotlin", naoVazio, tamanhoMinimo, semEspaco)) // true
println(validar("ab", naoVazio, tamanhoMinimo)) // false
println(validar("tem espaco", naoVazio, semEspaco)) // false
}
Referência de função com ::
Você pode passar funções já existentes usando :::
fun ehPositivo(n: Int) = n > 0
fun main() {
val numeros = listOf(-2, -1, 0, 1, 2)
val positivos = numeros.filter(::ehPositivo)
println(positivos) // [1, 2]
}
Funções com receiver como parâmetro
Higher-order functions podem receber funções com receiver, criando blocos de código que têm acesso ao contexto de um objeto específico:
fun configurar(builder: StringBuilder.() -> Unit): String {
val sb = StringBuilder()
sb.builder()
return sb.toString()
}
fun main() {
val html = configurar {
append("<html>")
append("<body>")
append("<h1>Kotlin Brasil</h1>")
append("</body>")
append("</html>")
}
println(html)
}
Esse padrão é a base das DSLs em Kotlin. O bloco passado como argumento executa no contexto do StringBuilder, tendo acesso direto a todos os seus métodos.
Composição de funções
Higher-order functions permitem compor funções menores para criar operações mais complexas:
fun <T> compor(
f: (T) -> T,
g: (T) -> T
): (T) -> T {
return { valor -> f(g(valor)) }
}
fun main() {
val dobrar: (Int) -> Int = { it * 2 }
val somar10: (Int) -> Int = { it + 10 }
val dobrarEsomar = compor(somar10, dobrar)
println(dobrarEsomar(5)) // 20 (5*2=10, 10+10=20)
val somarEdobrar = compor(dobrar, somar10)
println(somarEdobrar(5)) // 30 (5+10=15, 15*2=30)
}
Casos de Uso no Mundo Real
- Middleware em servidores web: frameworks como Ktor usam higher-order functions para definir pipelines de processamento de requisições, onde cada middleware recebe a próxima função do pipeline como parâmetro.
- Callbacks de eventos no Android: listeners de clique, animações e operações assíncronas são implementados como higher-order functions, substituindo interfaces anônimas do Java por lambdas concisas.
- Estratégias de retry e circuit breaker: funções que encapsulam a lógica de repetição recebem a operação a ser executada como parâmetro, permitindo aplicar políticas de retry a qualquer chamada de rede ou banco de dados.
- Transformação de dados em pipelines: operações de ETL e processamento de dados usam cadeias de higher-order functions como
map,flatMap,groupByefoldpara transformar coleções de forma declarativa.
Boas Práticas
- Marque higher-order functions com
inlinequando elas recebem lambdas e são chamadas com frequência, para evitar a criação de objetosFunctiondesnecessários. - Use tipos de função descritivos com typealias para melhorar a legibilidade:
typealias Validador = (String) -> Booleané mais claro que repetir(String) -> Booleanem vários lugares. - Prefira trailing lambda syntax quando a função tem apenas um parâmetro lambda ou quando o lambda é o parâmetro mais importante.
- Mantenha os tipos de função simples. Se uma higher-order function precisa de mais de 3 ou 4 parâmetros na função recebida, considere criar uma data class ou interface.
- Documente o contrato esperado do parâmetro de função, especialmente se ele tem efeitos colaterais ou restrições de thread.
Erros Comuns
- Esquecer o overhead de lambdas não-inline: cada lambda passada como argumento para uma função não-inline cria um objeto na heap. Em loops apertados, isso pode causar pressão no garbage collector. Use
inlinenesses casos. - Aninhar muitas higher-order functions: código como
lista.map { }.filter { }.flatMap { }.groupBy { }.mapValues { }pode se tornar difícil de depurar. Quebre em variáveis intermediárias quando a cadeia ficar longa. - Confundir referência de função com chamada de função:
::minhaFuncaoé uma referência (pode ser passada como argumento), enquantominhaFuncao()é uma chamada (executa e retorna o resultado). - Não considerar a nullable na assinatura: se a função recebida pode ser nula, o tipo deve ser
((Int) -> Int)?. Sem isso, o compilador não aceitanullcomo argumento. - Capturar variáveis mutáveis em lambdas: lambdas que capturam variáveis
varpodem causar comportamentos inesperados se executadas em threads diferentes ou em momentos posteriores.
Perguntas Frequentes
Qual a diferença entre higher-order function e lambda? Uma higher-order function é uma função que recebe ou retorna outras funções. Uma lambda é uma forma de criar uma função anônima. Lambdas são frequentemente passadas como argumento para higher-order functions, mas são conceitos distintos.
Higher-order functions afetam a performance? Sem o modificador inline, cada lambda cria um objeto na JVM. Para funções chamadas poucas vezes, o impacto é desprezível. Para hot paths (loops internos, processamento de alto volume), use inline para eliminar esse overhead.
Posso usar higher-order functions com coroutines? Sim. Você pode receber funções suspensas como parâmetro usando o tipo suspend () -> T. Isso é muito comum em APIs de coroutines, como withContext, launch e async.
O que é trailing lambda syntax? Quando o último parâmetro de uma função é uma lambda, você pode escrevê-la fora dos parênteses. Se a lambda é o único argumento, os parênteses podem ser omitidos completamente: lista.forEach { println(it) }.
Termos Relacionados
- Lambda — funções anônimas frequentemente passadas como argumento para higher-order functions
- Fun — palavra-chave usada para declarar higher-order functions
- Inline — modificador que otimiza higher-order functions eliminando o overhead de lambdas
- Flow — API baseada em higher-order functions para streams de dados assíncronos
Higher-order functions são o alicerce da programação funcional em Kotlin. Elas tornam o código mais flexível, reutilizável e expressivo.