Bloki init i wywołanie super w Kotlinie odpowiadają za poprawną inicjalizację obiektów z uwzględnieniem hierarchii klas. To podstawowe mechanizmy, które wpływają na działanie konstrukcji i dziedziczenie.
W Javie konstruktory można wywoływać jawnie za pomocą super(...) lub domyślnie niejawnie. W Kotlinie wszystko wygląda inaczej: istnieje wyraźny podział między konstruktorami głównymi a drugorzędnymi, a blok init jest używany do inicjalizacji.
Główną trudnością jest poprawne zrozumienie, kiedy inicjalizowane są właściwości, kiedy wywoływane są bloki init i jak ustalana jest kolejność wywoływania konstruktorów, szczególnie w przypadku dziedziczenia klas i kombinowania konstruktorów głównych oraz drugorzędnych.
Przykład kodu:
open class Base(val name: String) { init { println("Base init: $name") } } class Derived(name: String, val age: Int): Base(name) { init { println("Derived init: $name, $age") } } fun main() { Derived("Ivan", 25) } // Wyjście: // Base init: Ivan // Derived init: Ivan, 25
Kluczowe cechy:
W jakiej kolejności wykonywane są bloki init w łańcuchu dziedziczenia?
Najpierw wykonywane są bloki init superklasy po zainicjowaniu jej właściwości, a następnie bloki init podklasy po własnych właściwościach.
Przykład kodu:
open class A { init { println("A") } } class B: A() { init { println("B") } } fun main() { B() } // A -> B
Czy można wywołać super() nie z konstruktora w Kotlinie?
Nie, wywołanie superklasy odbywa się tylko jako część deklaracji konstruktora, dostarczając parametry do superkonstruktora.
Czy inicjalizacja konstruktorów drugorzędnych może nie wywoływać głównego w Kotlinie?
Nie, wszystkie konstruktory drugorzędne muszą delegować wywołanie do innego konstruktora drugorzędnego lub głównego. Główny, z kolei, wywołuje superkonstruktor.
Programista próbuje uzyskać dostęp do właściwości, inicjalizowanej w podklasie, w bloku init superklasy:
open class A { init { println(f()) } open fun f() = "A" } class B : A() { private val s = "B" override fun f() = s }``` **Plusy:** - Wykorzystanie polimorfizmu. **Minusy:** - Właściwość s jeszcze nie została zainicjowana podczas wywołania init superklasy – wynik to null lub błąd. ## Pozytywny przypadek Podzielenie inicjalizacji tak, aby nie wywoływać metod ani właściwości, które można nadpisywać z konstruktora: ```kotlin open class A { init { println("init A") } open fun f() = "A" } class B : A() { private val s = "B" override fun f() = s init { println(f()) } }``` **Plusy:** - Brak odniesień do niezainicjowanych danych; - Gwarantowana kolejność inicjalizacji. **Minusy:** - Zwiększa się ilość kodu potrzebnego do poprawnej inicjalizacji.