W Kotlinie istnieje wbudowane wsparcie dla delegowanych właściwości (delegated properties). Mechanizm by pozwala delegować getter/setter dowolnej właściwości do wyspecjalizowanego obiektu — delegata. Najbardziej znanymi delegatami są: lazy, observable, vetoable oraz delegaty niestandardowe.
Zalety:
Przykład delegata użytkownika:
class UpperCaseDelegate { private var value: String = "" operator fun getValue(thisRef: Any?, property: KProperty<*>): String = value operator fun setValue(thisRef: Any?, property: KProperty<*>, newValue: String) { value = newValue.uppercase() } } class Person { var name: String by UpperCaseDelegate() }
Ograniczenia:
Czy można używać delegata, który wymaga dostępu do kontekstu (na przykład Android Context), w właściwościach obiektów towarzyszących lub na poziomie najwyższym?
Często odpowiadają błędnie, że „można zawsze, czemu nie?”
Prawidłowa odpowiedź: Nie, ponieważ obiekty towarzyszące i obiekty na poziomie najwyższym są inicjalizowane przed inicjalizacją instancji klasy lub aplikacji, co może prowadzić do błędów związanych z dostępem do niezinicjowanego kontekstu. Delegaty, które wymagają dostępu do instancji, powinny być używane tylko w właściwościach klas.
Historia
Delegowanie leniwej inicjalizacji w Android-ViewModel: Programista przeniósł heavy-lazy delegata do obiektu towarzyszącego. W niektórych sytuacjach (po aktualizacji SDK) aplikacja zaczęła się zawieszać podczas inicjalizacji — kontekst nie był jeszcze dostępny, a delegat już wykonał swoje „init”.
Historia
Błędna serializacja z delegatami: Użyto niestandardowego delegata do przechowywania danych, jednak zawierał nie_serializowalne odniesienia do kontekstu. Podczas próby serializacji wystąpiły błędy i utrata danych.
Historia
Delegat Observable z błędnym callbackiem: Programista użył Delegates.observable do monitorowania stanu, w środku lambdy przypisano nową wartość, co doprowadziło do zapętlenia i StackOverflowError w czasie działania.