Czyli o obiektach towarzyszących w Kotlinie sobie dziś porozmawiamy. Nie od dziś wiadomo, że jak mamy jakiś problem z Javą to rozwiązaniem jest stworzenie ProblemFactory, która będzie zawierać statyczną metodę create. Kończymy z całą dzielnicą przemysłową w naszym kodzie. Nie to, żeby fabryki były złe. One są dobre, a nawet powiem więcej, one są bardzo sensowne, jeżeli tylko korzystamy z DDD w naszym projekcie. Dlaczego i po co to już osobna kwestia i dziś nie będę tego omawiać.

Trochę innym zagadnieniem są klasy narzędziowe, które posiadają całe litanie metod statycznych. Rzecz w tym, że w Kotlinie nie ma metod statycznych. Ich rolę przejmują właśnie obiekty towarzyszące… tak, wiem „taka debilna scala i po co nam to”…

Definiowanie

By zdefiniować obiekt towarzyszący, musimy umieścić go w klasie, której ma towarzyszyć oraz użyć słowa kluczowego companion:

Listing 1. Obiekt towarzyszący

class MyService{

    private constructor()

    companion object {

        fun create(): MyService = MyService()
    }
}

W ten sposób zdefiniowaliśmy najprostszy obiekt towarzyszący. Ma on domyślną nazwę Companion, ale oczywiście możemy używać własnych nazw. Tylko po co skoro rzadko będziemy odwoływać się do niej bezpośrednio z kodu?

Użycie

Jak już o użyciu to w trakcie kompilacji zostaną wygenerowane odpowiednie metody, które zostaną dołączone do naszej klasy. Zatem można napisać:

Listing 2. Użycie obiektów towarzyszących

fun main(args: Array<String>) {
    MyService.create();
}

I będzie to działać poprawnie. Rzecz w tym, że nie z poziomu javy nie będą one widoczne. Technicznie klasa MyService będzie mieć tylko pole Companion, za pomocą którego będziemy odwoływać się do metody create. Niby ok, ale można by to jakoś uprościć. W tym celu należy dodać adnotację @JvmStatic do metody, która zostanie wygenerowana w klasie serwisowej:

Listing 3. Obiekt towarzyszący z delegacją

class MyService{

    private constructor()

    companion object {

        @JvmStatic
        fun create(): MyService = MyService()
    }
}

Oczywiście obiekt towarzyszący to nadal normalna klasa, a zatem możne ona implementować interfejsy:

Listing 4. Obiekt towarzyszący implementujący interfejs Factory

interface Factory<T>{

    fun create():T
}

class MyService{

    private constructor()

    companion object :Factory<MyService>{

        override fun create(): MyService = MyService()
    }
}

Kod jest poglądowy i nie należy w ten sposób robić fabryk, ale chodzi tu o samą mechanikę związaną z implementacją.

Podsumowanie

Obiekty towarzyszące są w pewien sposób lepszym podejściem do kwestii statycznej zawartości w klasie. W Kotlinie ich implementacja jest bardzo prosta i intuicyjna. Bez większych przeszkód można ich używać. Zapewnienie współpracy z kodem w Javie też nie jest uciążliwe. Same plusy.