Sobes.tech
Middle

Czy możesz wyjaśnić zasadę działania delegata w klasie w języku Kotlin?

sobes.tech AI

Odpowiedź od AI

Delegat w Kotlinie to obiekt, który przejmuje wykonanie określonego zadania lub deleguje wywołania metod do innego obiektu.

Główne scenariusze użycia:

  1. Delegacja właściwości: Przekazywanie kontroli nad getterami i setterami właściwości do innego obiektu.

    • lazy: Obliczanie wartości właściwości przy pierwszym dostępie.
    • observable: Wykonywanie akcji przy zmianie wartości właściwości.
    • vetoable: Zapobieganie zmianie wartości właściwości na podstawie warunku.
    class Example {
        // Obliczane tylko przy pierwszym dostępie
        val lazyValue: String by lazy {
            println("Obliczam!")
            "Cześć"
        }
    
        // Wywoływane przy każdej zmianie wartości
        var name: String by Delegates.observable("<brak imienia>") { prop, old, new ->
            println("Imię zmieniło się z $old na $new")
        }
    }
    
  2. Delegacja interfejsów: Implementacja interfejsu poprzez przekierowanie wywołań metod do innego obiektu.

    interface Base {
        fun print()
    }
    
    class BaseImpl(val x: Int) : Base {
        override fun print() {
            println(x)
        }
    }
    
    // Implementuje Base, delegując wywołania do BaseImpl
    class Derived(b: Base) : Base by b
    
    fun main() {
        val b = BaseImpl(10)
        Derived(b).print() // Wypisze 10
    }
    
  3. Delegacja funkcji: Chociaż nie ma jawnej składni do delegowania pojedynczych funkcji, jak dla właściwości czy interfejsów, jest to realizowane koncepcyjnie przez kompozycję i wywołania metod innego obiektu.

Jak działa delegacja właściwości:

Kompilator Kotlin generuje kod pomocniczy. Dla właściwości delegowanej do obiektu delegate, kompilator generuje ukrytą właściwość i wywołuje metody getValue i setValue (dla właściwości zmiennych) na obiekcie delegate, przekazując odpowiedni kontekst (instancję klasy, metadane właściwości).

// Przykład pseudokodu, jak przetwarza to kompilator: val p: String by delegate
/*
private val _p: Delegate<Any?, String> = delegate_initializer
val p: String
    get() = _p.getValue(this, ::p) // Wywołanie getValue na delegacie
*/

Obiekt delegujący musi mieć odpowiednie metody:

  • getValue(thisRef, property: KProperty<*>): T dla właściwości typu T. thisRef to instancja klasy, property to metadane właściwości.
  • setValue(thisRef, property: KProperty<*>, value: T) dla właściwości zmiennych.

Zalety korzystania z delegatów:

  • Ponowne użycie kodu: Wspólna logika (leniwe inicjalizowanie, obserwacja zmian) jest wyodrębniana do osobnego delegata i wielokrotnie wykorzystywana.
  • Poprawa czytelności: Kod staje się mniej obszerny, boilerplate ukrywa się wewnątrz delegata.
  • Modularność: Logika zarządzania właściwościami lub implementacji interfejsów jest oddzielona od głównej logiki klasy.