Les blocs init et l'appel de super en Kotlin sont responsables de l'initialisation correcte des objets en tenant compte de la hiérarchie des classes. Ce sont des mécanismes fondamentaux qui influencent le fonctionnement des constructions et de l'héritage.
En Java, les constructeurs peuvent être appelés explicitement à l'aide de super(...) ou implicitement par défaut. En Kotlin, tout est différent : il existe une séparation claire entre les constructeurs primaires et secondaires, et le bloc init est utilisé pour l'initialisation.
La principale difficulté est de comprendre quand les propriétés sont initialisées, quand les blocs init sont appelés et comment l'ordre d'appel des constructeurs est établi, en particulier lors de l'héritage des classes et de la combinaison des constructeurs primaires/secondaires.
Exemple de code :
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) } // Sortie : // Base init: Ivan // Derived init: Ivan, 25
Caractéristiques clés :
Dans quel ordre les blocs init sont-ils exécutés dans la chaîne d'héritage ?
D'abord, les blocs init de la super-classe sont exécutés après l'initialisation de ses propriétés, puis les blocs init de la sous-classe après ses propres propriétés.
Exemple de code :
open class A { init { println("A") } } class B: A() { init { println("B") } } fun main() { B() } // A -> B
Peut-on appeler super() en dehors d'un constructeur en Kotlin ?
Non, l'appel du super-classe se fait uniquement dans le cadre de la déclaration du constructeur en fournissant des paramètres au super-constructeur.
L'initialisation d'un constructeur secondaire peut-elle ne pas appeler le constructeur primaire en Kotlin ?
Non, tous les constructeurs secondaires doivent déléguer l'appel soit à un autre constructeur secondaire, soit au constructeur primaire. Le primaire, à son tour, appelle le super-constructeur.
Un développeur essaie d'accéder à une propriété, initialisée dans une sous-classe, dans le bloc init de la super-classe :
open class A { init { println(f()) } open fun f() = "A" } class B : A() { private val s = "B" override fun f() = s }``` **Avantages :** - Utilisation du polymorphisme. **Inconvénients :** - La propriété s n'est pas encore initialisée lors de l'appel du init de la super-classe - le résultat est null ou une erreur. ## Cas positif Séparation de l'initialisation pour éviter d'appeler des méthodes ou des propriétés redéfinies depuis le constructeur : ```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()) } }``` **Avantages :** - Pas d'accès à des données non initialisées ; - Ordre d'initialisation garanti. **Inconvénients :** - Augmentation du volume de code pour une initialisation correcte.