Sobes.tech
Middle

Podes explicar o princípio de funcionamento de um delegado numa classe em Kotlin?

sobes.tech IA

Resposta da IA

Um delegado em Kotlin é um objeto que assume a execução de uma tarefa específica ou delega chamadas de métodos a outro objeto.

Cenários principais de uso:

  1. Delegação de propriedades: Transferir o controlo dos getters e setters de uma propriedade para outro objeto.

    • lazy: Avaliação do valor da propriedade na primeira acesso.
    • observable: Executar uma ação quando o valor da propriedade muda.
    • vetoable: Impedir a mudança do valor da propriedade com base numa condição.
    class Example {
        // Calculado apenas na primeira acesso
        val lazyValue: String by lazy {
            println("A calcular!")
            "Olá"
        }
    
        // É chamado em cada mudança de valor
        var name: String by Delegates.observable("<sem nome>") { prop, old, new ->
            println("O nome mudou de $old para $new")
        }
    }
    
  2. Delegação de interfaces: Implementação de uma interface através do redireccionamento de chamadas de métodos para outro objeto.

    interface Base {
        fun print()
    }
    
    class BaseImpl(val x: Int) : Base {
        override fun print() {
            println(x)
        }
    }
    
    // Implementa Base, delegando chamadas a BaseImpl
    class Derived(b: Base) : Base by b
    
    fun main() {
        val b = BaseImpl(10)
        Derived(b).print() // Vai imprimir 10
    }
    
  3. Delegação de funções: Embora não exista uma sintaxe explícita para delegar funções individuais como para propriedades ou interfaces, isso é realizado conceptualmente através de composição e chamadas de métodos de outro objeto.

Como funciona a delegação de propriedades:

O compilador Kotlin gera código auxiliar. Para uma propriedade delegada a um objeto delegate, o compilador gera uma propriedade oculta e chama os métodos getValue e setValue (para propriedades mutáveis) no objeto delegate, passando o contexto necessário (instância da classe, metadados da propriedade).

// Exemplo de pseudocódigo, como o compilador processa: val p: String by delegate
/*
private val _p: Delegate<Any?, String> = delegate_initializer
val p: String
    get() = _p.getValue(this, ::p) // Chamada a getValue no delegado
*/

O objeto delegado deve ter os métodos correspondentes:

  • getValue(thisRef, property: KProperty<*>): T para propriedade do tipo T. thisRef é a instância da classe, property são os metadados da propriedade.
  • setValue(thisRef, property: KProperty<*>, value: T) para propriedade mutável.

Vantagens do uso de delegados:

  • Reutilização de código: A lógica comum (inicialização preguiçosa, observação de mudanças) é extraída para um delegado separado e reutilizada.
  • Melhoria na legibilidade: O código torna-se menos verboso, o boilerplate fica escondido dentro do delegado.
  • Modularidade: A lógica de gestão de propriedades ou implementação de interfaces é separada da lógica principal da classe.