Introdução: Um pequeno operador — uma grande magia
\n\nVocê já olhou para o código de outra pessoa em Kotlin e viu uma construção estranha como val name by lazy { ... } ou class Derived(Base by impl)? Se sim, você certamente se perguntou: «O que a palavra-chave by faz em Kotlin?». Esta pergunta é uma das mais populares no Stack Overflow, e não é à toa. by não é apenas açúcar sintático, mas um poderoso mecanismo de delegação que pode simplificar drasticamente seu código, torná-lo mais limpo e eliminar toneladas de código boilerplate.
Neste artigo, vamos analisar by de ponta a ponta. Você aprenderá como funcionam a delegação de propriedades (property delegation) e a delegação de classes (class delegation). Responderemos às perguntas mais frequentes de iniciantes e mostraremos exemplos reais que você pode aplicar imediatamente em seus projetos. Vamos lá!
O que é delegação em Kotlin? (A ideia principal)
\n\nAntes de mergulharmos nos detalhes, vamos entender a essência. Delegação é um padrão de design no qual um objeto transfere a execução de sua tarefa para outro objeto auxiliar. O Kotlin incorporou esse padrão diretamente na linguagem usando a palavra-chave by. Em vez de escrever o código "manualmente", você diz ao compilador: «Ei, deixe essa coisa fazer tudo por mim».
Em Kotlin, existem dois tipos principais de delegação:
\n- \n
- Delegação de propriedades — quando o getter/setter de uma propriedade é passado para outro objeto. \n
- Delegação de classes — quando a implementação de uma interface é passada para outro objeto. \n
Ambas as opções usam a mesma palavra-chave — by. Vamos analisar cada uma.
1. Delegação de propriedades (Property Delegation): by para campos
\n\nEste é o uso mais popular de by. Ele permite reutilizar a lógica para getters e setters de propriedades. Em vez de escrever código repetitivo toda vez, você usa delegados prontos ou personalizados.
Exemplo 1: by lazy — inicialização preguiçosa
\n\nImagine que você tem uma propriedade que demora para ser criada (por exemplo, carregar dados da rede). Você não quer calculá-la ao criar a classe, mas sim no primeiro acesso. by lazy faz exatamente isso:
val heavyData: String by lazy {\n println("Calculando dados...")\n "Resultado muito pesado"\n}\n\nfun main() {\n println("Início")\n println(heavyData) // Primeiro acesso: calcula\n println(heavyData) // Segundo acesso: apenas pega do cache\n}\n\nSaída:
\nInício\nCalculando dados...\nResultado muito pesado\nResultado muito pesado\n\nO que aconteceu aqui? A palavra-chave by diz: «A propriedade heavyData delega sua implementação ao objeto lazy { ... }». Esse objeto garante que o bloco de código será executado apenas uma vez, no primeiro acesso, e então armazenará o resultado. Isso é ideal para configurações, conexões de banco de dados e outros objetos "caros".
Exemplo 2: by observable — monitorando mudanças
\n\nQuer executar alguma ação toda vez que uma propriedade muda? Use Delegates.observable da biblioteca padrão:
import kotlin.properties.Delegates\n\nvar watchedValue: Int by Delegates.observable(0) { property, oldValue, newValue ->\n println("Antigo: $oldValue, Novo: $newValue")\n}\n\nfun main() {\n watchedValue = 10 // Antigo: 0, Novo: 10\n watchedValue = 42 // Antigo: 10, Novo: 42\n}\n\nEsse padrão é indispensável para programação reativa, validação de dados ou sincronização de UI. A propósito, by aqui funciona exatamente da mesma