ProgrammationDéveloppeur Kotlin Senior

Comment le mot-clé 'super' fonctionne-t-il en Kotlin et en quoi diffère-t-il de son utilisation en Java ? Dans quels cas des ambiguïtés peuvent survenir lors de la mise en œuvre multiple des interfaces et comment les résoudre ?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse

En Kotlin, le mot-clé super est utilisé pour faire référence à l'implémentation d'une méthode ou d'une propriété de la superclasse ou de l'interface implémentée. Contrairement à Java, Kotlin permet de spécifier explicitement par quelle interface ou classe appeler l'implémentation de la méthode, ce qui est particulièrement important lors du "conflit" de plusieurs interfaces avec les mêmes méthodes.

Particularité : Si une classe implémente plusieurs interfaces qui définissent une méthode avec la même signature et qu'une implémentation par défaut est présente, il est nécessaire d'indiquer explicitement quelle implémentation utiliser.

Exemple :

interface A { fun foo() = println("A") } interface B { fun foo() = println("B") } class C : A, B { override fun foo() { super<A>.foo() // Appel explicite à A.foo super<B>.foo() // Appel explicite à B.foo } }

Cela n'est pas directement pris en charge en Java (Java ne permet pas aux interfaces d'avoir des champs/implémentations de méthodes jusqu'à Java 8, et même dans Java 8 - les mécanismes sont différents).

Question piège

"Peut-on en Kotlin faire appel à l'implémentation d'une méthode de la superclasse à travers plusieurs niveaux d'héritage ?"

  • Une erreur fréquente : on croit qu'on peut accéder à l'implémentation à travers une classe intermédiaire : super<Intermediate>.foo(), mais ce n'est pas le cas - on peut appeler uniquement la superclasse immédiate ou l'interface qui implémente cette méthode directement.

Exemple :

open class Base { open fun foo() = println("Base") } open class Mid : Base() { override fun foo() { println("Mid"); super.foo() } } class Child: Mid() { override fun foo() { super.foo() // appellera Mid.foo() // super<Base>.foo() — erreur de compilation } }

Exemples d'erreurs réelles dues à la méconnaissance des subtilités du sujet


Histoire

Dans un grand projet, les interfaces Logger et Auditor définissaient toutes deux la méthode record(). Lors de l'implémentation d'une classe héritant des deux interfaces, le programmeur n'a pas redéfini explicitement record(), ce qui a entraîné une erreur de compilation "La classe doit redéfinir la fonction publique ouverte record()". Il a fallu implémenter la méthode manuellement et déléguer explicitement à l'interface correspondante.


Histoire

En essayant d'appeler l'implémentation de la méthode de la superclasse à travers plusieurs couches intermédiaires (par exemple, super<Base>.foo()), une erreur de compilation est survenue. Le développeur ne connaissait pas les limitations de Kotlin et ne comprenait pas pourquoi l'appel direct était impossible.


Histoire

Après la mise à jour de l'API, l'une des interfaces a ajouté une implémentation de fonction par défaut, qui coïncidait avec une autre interface. L'ancien code ne compilait plus en raison d'un conflit d'implémentations - il a fallu résoudre explicitement le conflit en implémentant la méthode soi-même et en choisissant quelles implémentations de superinterfaces appeler à l'intérieur.