O que é Delegation em Kotlin?

Delegation (delegação) é um padrão de design onde um objeto repassa responsabilidades para outro. Em Kotlin, a delegação é suportada nativamente com a palavra-chave by, tanto para classes quanto para propriedades.

Em vez de herdar de uma classe, você delega a implementação para uma instância. É o famoso princípio “composição sobre herança” que fica muito fácil de aplicar em Kotlin.

Delegação de classe

interface Repositorio {
    fun salvar(dados: String)
    fun buscar(): String
}

class RepositorioBanco : Repositorio {
    override fun salvar(dados: String) = println("Salvando no banco: $dados")
    override fun buscar() = "Dados do banco"
}

class RepositorioComLog(repo: Repositorio) : Repositorio by repo {
    override fun salvar(dados: String) {
        println("[LOG] Operação de salvar iniciada")
        // Delega para o repositório original, mas não automaticamente neste override
    }
    // buscar() é delegado automaticamente
}

fun main() {
    val repo = RepositorioComLog(RepositorioBanco())
    println(repo.buscar()) // Dados do banco
    repo.salvar("teste")   // [LOG] Operação de salvar iniciada
}

O by repo faz com que todos os métodos da interface sejam delegados automaticamente. Você só sobrescreve o que quiser customizar.

Delegação de propriedade

Kotlin oferece delegates prontos pra uso:

import kotlin.properties.Delegates

class Configuracao {
    // Inicialização preguiçosa
    val conexao: String by lazy {
        println("Conectando ao banco...")
        "Conexão estabelecida"
    }

    // Observável — executa bloco quando muda
    var tema: String by Delegates.observable("claro") { _, antigo, novo ->
        println("Tema mudou de '$antigo' para '$novo'")
    }

    // Não pode ser lido antes de ser atribuído
    var usuario: String by Delegates.notNull()
}

fun main() {
    val config = Configuracao()

    println(config.conexao) // "Conectando..." + "Conexão estabelecida"
    println(config.conexao) // "Conexão estabelecida" (já inicializou)

    config.tema = "escuro"  // Tema mudou de 'claro' para 'escuro'

    config.usuario = "Karina"
    println(config.usuario) // Karina
}

Criando seu próprio delegate

import kotlin.reflect.KProperty

class FormatadoDelegate {
    private var valor = ""

    operator fun getValue(thisRef: Any?, property: KProperty<*>): String = valor
    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
        valor = value.trim().lowercase()
    }
}

class Formulario {
    var email: String by FormatadoDelegate()
}

fun main() {
    val form = Formulario()
    form.email = "  KARINA@Email.COM  "
    println(form.email) // karina@email.com
}

Delegation em Kotlin é poderoso pra reduzir boilerplate, adicionar comportamentos transversais e manter o código organizado.