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, groupBy e fold para transformar coleções de forma declarativa.

Boas Práticas

  • Marque higher-order functions com inline quando elas recebem lambdas e são chamadas com frequência, para evitar a criação de objetos Function desnecessários.
  • Use tipos de função descritivos com typealias para melhorar a legibilidade: typealias Validador = (String) -> Boolean é mais claro que repetir (String) -> Boolean em 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 inline nesses 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), enquanto minhaFuncao() é 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 aceita null como argumento.
  • Capturar variáveis mutáveis em lambdas: lambdas que capturam variáveis var podem 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.