---
title: "Callback em Kotlin: O que É e Como Funciona | Kotlin Brasil"
url: "https://kotlin.dev.br/glossario/callback/"
markdown_url: "https://kotlin.dev.br/glossario/callback.MD"
description: "Aprenda o que são callbacks em Kotlin, como usar lambdas e interfaces funcionais para comunicação entre componentes."
date: "2025-08-03"
author: "Karina Melo"
---

# Callback em Kotlin: O que É e Como Funciona | Kotlin Brasil

Aprenda o que são callbacks em Kotlin, como usar lambdas e interfaces funcionais para comunicação entre componentes.


## O que é Callback em Kotlin?

Um **callback** é uma função passada como argumento para outra função, que sera executada em um momento posterior -- geralmente quando uma operação assíncrona termina ou quando um evento específico ocorre. Em Kotlin, callbacks são implementados de forma elegante usando **lambdas**, **funções de ordem superior** e **interfaces funcionais**.

O conceito e antigo e existe em praticamente todas as linguagens, mas em Kotlin ele ganha uma sintaxe limpa e concisa que torna o código muito mais legivel do que em linguagens como Java.

### Callback básico com lambda

A forma mais simples de callback em Kotlin e passar uma lambda como parametro:

```kotlin
fun buscarDados(onSucesso: (String) -> Unit, onErro: (Exception) -> Unit) {
    try {
        // Simula busca de dados
        val resultado = "Dados carregados"
        onSucesso(resultado)
    } catch (e: Exception) {
        onErro(e)
    }
}

fun main() {
    buscarDados(
        onSucesso = { dados -> println("Sucesso: $dados") },
        onErro = { erro -> println("Erro: ${erro.message}") }
    )
}
```

Aqui, `onSucesso` e `onErro` são callbacks. A função `buscarDados` recebe as duas lambdas e chama a apropriada dependendo do resultado da operação.

### Callback com interface funcional

Em cenários de interoperabilidade com Java ou quando você quer um contrato mais explicito, você pode usar interfaces funcionais:

```kotlin
fun interface OnResultadoListener {
    fun onResultado(dados: String)
}

class Repositorio {
    fun carregar(listener: OnResultadoListener) {
        // Simula processamento
        val resultado = "Dados do servidor"
        listener.onResultado(resultado)
    }
}

fun main() {
    val repo = Repositorio()

    // Usando SAM conversion
    repo.carregar { dados ->
        println("Recebido: $dados")
    }

    // Ou de forma explicita
    repo.carregar(object : OnResultadoListener {
        override fun onResultado(dados: String) {
            println("Recebido: $dados")
        }
    })
}
```

A palavra-chave `fun interface` permite que o Kotlin converta automaticamente uma lambda em uma implementação da interface (SAM conversion), mantendo o código conciso.

### Callback para eventos de UI

Um uso clássico de callbacks e em componentes de interface grafica, como botoes:

```kotlin
class Botao {
    private var onClickListener: (() -> Unit)? = null

    fun setOnClickListener(listener: () -> Unit) {
        onClickListener = listener
    }

    fun clicar() {
        onClickListener?.invoke()
    }
}

fun main() {
    val botao = Botao()
    botao.setOnClickListener {
        println("Botao foi clicado!")
    }
    botao.clicar() // Botao foi clicado!
}
```

Esse padrão e fundamental no desenvolvimento Android, onde callbacks conectam acoes do usuário a lógica de negócio.

### Callback hell e como evitar

O problema clássico com callbacks e o **callback hell** -- aninhamento excessivo que torna o código ilegivel:

```kotlin
// Callback hell -- evite isso
fun carregarTudo() {
    buscarUsuario { usuario ->
        buscarPedidos(usuario.id) { pedidos ->
            buscarDetalhes(pedidos.first().id) { detalhes ->
                buscarEndereco(detalhes.enderecoId) { endereco ->
                    println("Endereco: $endereco")
                }
            }
        }
    }
}
```

Em Kotlin, a solução moderna para esse problema são as **coroutines**, que permitem escrever código assíncrono de forma sequencial:

```kotlin
// Com coroutines -- muito mais limpo
suspend fun carregarTudo() {
    val usuario = buscarUsuario()
    val pedidos = buscarPedidos(usuario.id)
    val detalhes = buscarDetalhes(pedidos.first().id)
    val endereco = buscarEndereco(detalhes.enderecoId)
    println("Endereco: $endereco")
}
```

### Convertendo callbacks para coroutines

Quando você trabalha com APIs baseadas em callbacks (especialmente bibliotecas Java), pode converte-las para suspending functions usando `suspendCoroutine` ou `suspendCancellableCoroutine`:

```kotlin
import kotlinx.coroutines.*
import kotlin.coroutines.*

// API legada baseada em callback
fun buscarDadosLegado(callback: (Result<String>) -> Unit) {
    // Simula operacao assíncrona
    callback(Result.success("Dados legados"))
}

// Wrapper com coroutines
suspend fun buscarDadosSuspend(): String = suspendCancellableCoroutine { continuation ->
    buscarDadosLegado { resultado ->
        resultado
            .onSuccess { continuation.resume(it) }
            .onFailure { continuation.resumeWithException(it) }
    }
}

fun main() = runBlocking {
    val dados = buscarDadosSuspend()
    println(dados)
}
```

Essa técnica e extremamente útil ao migrar código legado para coroutines de forma incremental.

### Callback com tipo generico

Você pode criar callbacks reutilizaveis usando generics:

```kotlin
typealias Callback<T> = (Result<T>) -> Unit

fun <T> executarAsync(operacao: () -> T, callback: Callback<T>) {
    try {
        val resultado = operacao()
        callback(Result.success(resultado))
    } catch (e: Exception) {
        callback(Result.failure(e))
    }
}

fun main() {
    executarAsync(
        operacao = { 42 * 2 },
        callback = { resultado ->
            resultado
                .onSuccess { println("Resultado: $it") }
                .onFailure { println("Erro: ${it.message}") }
        }
    )
}
```

O `typealias` torna a assinatura mais legivel, e o uso de `Result` padroniza o tratamento de sucesso e erro.

### Quando usar callbacks

Callbacks ainda são úteis em vários cenários:

- **Interoperabilidade com Java**: muitas bibliotecas Java usam o padrão listener/callback, e você precisa se adaptar.
- **Eventos de UI**: cliques, gestos e outros eventos de interface são naturalmente modelados como callbacks.
- **APIs simples**: quando a operação e direta e não há aninhamento, um callback e perfeitamente adequado.
- **Bibliotecas e frameworks**: se você esta criando uma biblioteca que precisa ser usada por projetos que não usam coroutines.

Porem, para lógica assíncrona complexa com múltiplas etapas, prefira coroutines. Elas eliminam o aninhamento é fácilitam o tratamento de erros com `try-catch` normal.

### Casos de Uso no Mundo Real

1. **Listeners de eventos em Android**: toda interação do usuário com a interface -- cliques em botoes, mudancas em campos de texto, gestos de swipe -- e modelada como callback. O `setOnClickListener` do Android e o exemplo mais clássico, onde uma lambda e registrada para ser executada quando o usuário interage com o componente.

2. **Respostas de APIs de rede**: bibliotecas como Retrofit e OkHttp usam callbacks para notificar quando uma requisicao HTTP foi concluida. O callback recebe a resposta do servidor ou um objeto de erro, permitindo que o código que fez a requisicao processe o resultado sem bloquear a thread principal.

3. **Observadores de ciclo de vida**: no Android, callbacks como `onStart()`, `onResume()`, `onPause()` e `onDestroy()` notificam o componente sobre mudancas no ciclo de vida. Frameworks como o Lifecycle do AndroidX usam esse padrão para que bibliotecas reajam automaticamente a mudancas de estado da Activity ou Fragment.

4. **Processamento de arquivos e streams**: operações de leitura/escrita em arquivos grandes frequentemente usam callbacks para reportar progresso. Em vez de bloquear a thread até a operação terminar, o sistema notifica o chamador a cada bloco processado, permitindo exibir barras de progresso ou cancelar a operação.

### Boas Praticas

- Prefira lambdas com parametros nomeados na assinatura da função (`onSucesso`, `onErro`) em vez de um único callback generico. Nomes explicitos tornam a intencao clara e facilitam a leitura do código no ponto de chamada.
- Garanta que o callback seja chamado em exatamente um caminho de execução. Esquecer de chamar o callback em algum branch condicional causa bugs silenciosos, e chama-lo mais de uma vez pode causar comportamento inesperado.
- Ao trabalhar com callbacks em Android, sempre considere o ciclo de vida do componente. Cancele ou remova callbacks quando a Activity ou Fragment for destruído para evitar memory leaks e crashes por referência a contextos invalidos.
- Para APIs novas, prefira coroutines e `suspend fun` em vez de callbacks. Use `suspendCancellableCoroutine` para adaptar APIs legadas baseadas em callback para o mundo das coroutines, facilitando composicao e tratamento de erros.
- Utilize `typealias` para dar nomes significativos a tipos de callback complexos, como `typealias OnResultado<T> = (Result<T>) -> Unit`. Isso melhora a legibilidade das assinaturas de funções sem adicionar overhead.

### Perguntas Frequentes

**P: Quando devo usar callbacks em vez de coroutines em Kotlin?**
R: Use callbacks quando estiver trabalhando com interoperabilidade Java (bibliotecas que já usam o padrão listener), eventos de UI simples (cliques, gestos) ou quando estiver criando bibliotecas que precisam ser acessiveis a projetos que não usam coroutines. Para lógica assíncrona com múltiplas etapas, prefira coroutines.

**P: Como evitar memory leaks com callbacks em Android?**
R: Registre callbacks em `onStart()` ou `onResume()` e remova-os em `onStop()` ou `onPause()`. Evite usar lambdas que capturam referência a Activities ou Views. Se necessário, use `WeakReference` ou migre para `LiveData`/`Flow` que respeitam o ciclo de vida automaticamente.

**P: Qual a diferenca entre um callback e um listener em Kotlin?**
R: Na prática, sao o mesmo conceito. "Listener" e o termo mais comum no ecossistema Android e Java, geralmente implementado como uma interface com um ou mais métodos. "Callback" e o termo generico da programacao para qualquer função passada como argumento para ser chamada posteriormente. Em Kotlin, ambos podem ser implementados como lambdas.

**P: E possível converter qualquer API baseada em callback para coroutines?**
R: Sim, usando `suspendCancellableCoroutine`. Voce encapsula a chamada da API legada dentro dessa função, e no callback de sucesso chama `continuation.resume(valor)`, e no callback de erro chama `continuation.resumeWithException(exceção)`. Isso transforma a API em uma `suspend fun` que pode ser chamada de forma sequencial.

### Erros comuns

1. **Callback hell**: aninhar callbacks excessivamente. Use coroutines ou quebre a lógica em funções menores.

2. **Esquecer de chamar o callback**: se a função tem caminhos de execução diferentes (sucesso, erro, timeout), certifique-se de que o callback e chamado em todos eles.

3. **Chamar o callback mais de uma vez**: a maioria dos contratos espera que o callback seja chamado exatamente uma vez. Chamar múltiplas vezes pode causar comportamento inesperado.

4. **Nao tratar erros**: passar apenas um callback de sucesso e ignorar falhas. Sempre forneca um mecanismo para tratar erros.

5. **Reter referências de callback**: em Android, manter uma referência a um callback que contém uma referência a uma Activity pode causar memory leaks. Use `WeakReference` ou cancele o callback quando o componente for destruído.

### Termos relacionados

- **Lambda**: a forma mais comum de expressar callbacks em Kotlin, usando a sintaxe `{ parametros -> corpo }`.
- **Higher-Order Function**: funções que recebem ou retornam outras funções, o mecanismo que permite callbacks em Kotlin.
- **Coroutine**: alternativa moderna aos callbacks para programação assíncrona, eliminando o callback hell.
- **suspend**: modificador que transforma uma função em uma função suspensa, substituindo callbacks por retorno direto.
- **Flow**: stream reativo que substitui callbacks repetitivos por um fluxo de dados continuo.
- **SAM Conversion**: mecanismo que converte lambdas em implementacoes de interfaces funcionais Java.

Callbacks são um dos padrões mais fundamentais da programação. Em Kotlin, a combinacao de lambdas, funções de ordem superior e interfaces funcionais torna sua implementação elegante e expressiva, enquanto coroutines oferecem uma evolução natural para cenários mais complexos.
