Deklaracja stałych i użycie obiektów towarzyszących to ważne koncepcje w Kotlinie, które zastąpiły tradycyjne człony static z Javy oraz związane z nimi trudności przy przenikaniu paradygmatów OOP i programowania funkcyjnego.
Historia zagadnienia: W Javie dla stałych zazwyczaj używa się pól static final, a metody statyczne — do funkcji pomocniczych lub fabrycznych. W Kotlinie zamiast static wprowadzono object i companion object, a dla stałych kompilacyjnych — słowo kluczowe const.
Problem: Należy deklarować wartości, które nie zależą od instancji klasy, a także zorganizować metody fabryczne i stan statyczny, nie naruszając integralności OOP.
Rozwiązanie: Obiekty towarzyszące (companion object) deklarowane są wewnątrz klasy i pozwalają na umieszczanie członów wspólnych dla wszystkich instancji:
Przykład kodu:
class MyClass { companion object { const val DEFAULT_LIMIT = 10 fun create(): MyClass = MyClass() } } val limit = MyClass.DEFAULT_LIMIT val instance = MyClass.create()
Kluczowe cechy:
Czy companion object może mieć kilka instancji w klasie?
Nie, w jednej klasie może być tylko jeden companion object. Próba zadeklarowania drugiego spowoduje błąd kompilacji. Ale wewnątrz companion object można mieć dowolną liczbę metod/właściwości.
Czy można inicjalizować zmienne lateinit wewnątrz companion object?
Nie, ponieważ właściwości z const lub zmienne wewnątrz companion object muszą być inicjalizowane od razu lub być val/var z wyraźną inicjalizacją. lateinit nie jest dozwolony dla właściwości wewnątrz companion object.
Czy companion object może mieć własną nazwę i kiedy jest to wymagane?
Tak, nazwa companion object jest przypisywana jawnie, jeśli trzeba się do niego odwołać po nazwie lub, na przykład, implementować interfejsy. W innych przypadkach jest to opcjonalne. Przykład:
class Foo { companion object Factory { fun create(): Foo = Foo() } } val instance = Foo.Factory.create()
Całe funkcjonalności pomocnicze i globalne zmienne programu są umieszczane w companion object, używane są var zamiast val/const:
Zalety:
Wady:
Używane są tylko stałe kompilacyjne (const val) i czyste funkcje wewnątrz companion object, wszystko zmienne jest albo lokalizowane, albo przekazywane przez DI:
Zalety:
Wady: