Les fonctions d'extension (extension functions) sont l'une des principales différences entre Kotlin et Java, apparues pour la première fois avec les premières versions du langage, inspirées par C#. Elles permettent d'ajouter de nouvelles méthodes aux classes existantes sans nécessiter d'héritage ou de modification de la classe d'origine. Cela augmente considérablement l'expressivité et la concision du code.
En Java et dans de nombreux autres langages, il n'est pas possible d'ajouter des méthodes à une classe existante (par exemple, String ou List) sans utiliser l'héritage ou le modèle Decorator. Kotlin résout cette lacune grâce au mécanisme des fonctions d'extension.
De nombreuses classes de la bibliothèque (comme String, Int, List) ne peuvent pas être modifiées directement. Souvent, des méthodes supplémentaires sont nécessaires pour rendre le code plus lisible et éviter la duplication de la logique. Cependant, les fonctions d'extension ne modifient pas la classe réelle — elles sont créées statiquement et n’ont pas accès aux membres privés/protégés.
En Kotlin, la déclaration d'une fonction d'extension ressemble à ceci :
fun String.lastChar(): Char = this[this.length - 1]
On peut maintenant appeler cette méthode sur n'importe quel String :
"Kotlin".lastChar() // retournera 'n'
Une fonction d'extension peut-elle être redéfinie dans une classe dérivée ?
Non. Les fonctions d'extension ne sont pas des membres de la classe, elles ne peuvent pas être redéfinies via override : leur caractère statique se manifeste même pour les dérivés — le compilateur choisit la fonction en fonction du type de l'expression, et non du type réel de l'objet.
open class Base class Derived : Base() fun Base.foo() = "base" fun Derived.foo() = "derived" fun printFoo(b: Base) { println(b.foo()) } val d = Derived() printFoo(d) // affichera "base"
Les fonctions d'extension peuvent-elles changer l'état d'un objet ?
Les fonctions d'extension ne peuvent pas accéder à l'état privé/protégé, mais si le type est mutable (par exemple, List ou votre propre classe), elles peuvent modifier l'état externe accessible via des méthodes publiques.
fun MutableList<Int>.addTwice(elem: Int) { add(elem) add(elem) }
Les fonctions d'extension entrent-elles en conflit avec les membres de la classe ayant le même nom ?
Si le nom de l'extension coïncide avec celui d'une méthode réelle, la priorité revient au membre de la classe. L'extension ne sera appelée que si les membres sont absents ou invisibles.
class Foo { fun bar() = "member" } fun Foo.bar() = "extension" val f = Foo() println(f.bar()) // "member"
Un développeur étend la classe String standard avec une fonction définissant un format spécifique au projet et l'utilise partout — d'autres développeurs ne se rendent pas compte que ce n'est pas une opération standard de la classe String.
Avantages :
Inconvénients :
Extension d'une classe standard avec une méthode utilitaire qui est clairement localisée dans le paquet utils et bien documentée. Son utilisation est limitée à des zones de code explicitement définies.
Avantages :
Inconvénients :