Comme nous avons pu le voir dans le premier chapitre de ce cours, une des principales préoccupations de Kotlin est de pouvoir s’intégrer le plus facilement et proprement possible à du code existant écrit en Java.
D’ailleurs, même les programmes écrits uniquement en Kotlin utilisent TOUJOURS des librairies ou frameworks écrits en Java, comme le JDK ou le framework Android, par exemple. Les développeurs JetBrains, aussi performants soient-ils, ne pouvaient pas réécrire tous ces frameworks en Kotlin ! :D #FautPasPousserMéméDansLesOrties
Ainsi, il a fallu trouver une approche logicielle pour "étendre" les fonctionnalités de ces librairies ou frameworks Java actuels : les "extensions" en Kotlin ont alors été créées afin de répondre à ce besoin.
L’extension grâce aux fonctions
Afin d’étendre le comportement d’une classe, qu’elle soit écrite en Java ou en Kotlin, nous allons pouvoir, grâce au mécanisme d’extension que nous offre Kotlin, ajouter des fonctions qui seront perçues comme "appartenant" à la classe elle-même.
Par exemple, imaginons que nous souhaitions faire en sorte d'ajouter une méthode sayHello()
à toutes les variables de type String
. Comment allons-nous nous y prendre ?
Eh bien nous créons une classe MyString
(héritant de String
) et ajoutons à celle-ci la méthode sayHello()
? :honte:
Eh non ! Cette approche est Java-Friendly : en suivant votre approche, nous serions obligés d’utiliser la classe MyString
contenant notre nouvelle méthode pour gérer tous nos String
. C’est un peu contraignant, vous ne trouvez pas ?
Oui, c’est vrai…:euh:
Eh puis de toute manière, la classe String est final
, nous ne pouvons donc pas hériter d'elle ! :p
Cependant, grâce aux extensions en Kotlin, nous allons pouvoir "ajouter" la méthode sayHello()
à l’intérieur de la classe String
, sans modifier directement cette dernière (car, de toute manière, on ne pourrait pas, vu qu’elle se trouve dans le JDK ;) ) !
![Référence : Star wars](https://user.oc-static.com/upload/2018/06/12/15287833183935_luke.gif)
Eh si ! C’est d’ailleurs tout le pouvoir et l’intérêt des extensions... ;) Celles-ci vont d’ailleurs se comporter comme nos fameuses "top-level functions".
Ainsi, créons notre première extension, et ajoutons la fonction sayHello()
à la classe String
:
![Créez une extension en Kotlin](https://user.oc-static.com/upload/2023/02/06/16756736654025_16553887946621_p3c2-1a.png)
Une extension se déclare dans un fichier à part (1), que l’on nomme généralement d’après le nom de la classe que l’on souhaite étendre, suivi du mot "Extensions" ou "Ext". Dans l’absolu, cela n’a aucune importance (vous pourriez appeler le fichier "test", le résultat serait le même), mais cette approche permet tout de même d’améliorer la lisibilité et la compréhension de notre projet. :)
Puis, à l’intérieur de ce fichier, nous créons une "fonction d’extension" (2) qui permettra en quelque sorte "d’ajouter une méthode" dans la classe String
:
fun String sayHello() = print("This variable contains: $this")
Afin de déclarer une fonction d’extension, nous commençons comme d’habitude par le mot-clé fun
. Nous indiquons ensuite le nom de la classe que nous souhaitons étendre ( String
), que l’on appelle aussi le "type récepteur" (ou "Receiver Type", en anglais), suivi d’un point .
et du nom de la méthode que nous souhaitons créer ( sayHello
).
Nous renseignons à la suite le contenu de notre méthode (ici, un simple print
), et nous récupérons l’instance de l’objet utilisée dans cette extension grâce au mot-clé this
.
Pour terminer, nous appelons cette fonction (3) sur une variable de type String
. On remarque qu’un import vers cette extension a dû être réalisé afin d’indiquer au compilateur où la récupérer.
Ainsi, l’extension sayHello
sera résolue statiquement par le compilateur. Derrière toute cette "magie" se cache en réalité quelque chose d’assez simple : la méthode sayHello
est une méthode statique publique acceptant en unique paramètre un objet de type String
. ;)
D’ailleurs, pour vous le prouver, appelons cette méthode depuis une classe Java :
![Appeler une extension Kotlin en Java](https://user.oc-static.com/upload/2018/06/12/15287836354665_kotlin_extension_call_from_java.png)
Cela ne vous rappelle-t-il pas quelque chose ? :) C’est exactement le même principe que les "top-level functions" vues dans le précédent chapitre. Pour chaque fonction d’extension créée, le compilateur Kotlin créera l’équivalent en Java sous la forme d’une classe contenant des méthodes publiques statiques. Bien pensé, n'est-ce pas ?
Les fonctions d’extension peuvent accéder aux propriétés et méthodes de la classe qu’elles "étendent", comme si elles étaient placées à l’intérieur de celle-ci. En revanche, l’extension en Kotlin ne vous permettra pas de passer outre l’encapsulation de la classe ! Vous ne pourrez donc pas accéder aux méthodes ou aux propriétés définies comme private
, internal
(hors du module) ou protected
.
![Visibilité d'une fonction d'extension](https://user.oc-static.com/upload/2021/09/27/16327537109977_p3c2-1.png)
Sachez également qu’une extension ne pourra pas être surchargée : impossible donc d’utiliser le mot-clé override
pour modifier le comportement d’une extension déjà créée.
L’extension grâce aux propriétés
La fonctionnalité d’extension de Kotlin ne se limite pas qu’aux fonctions ! :) Vous pouvez aussi créer des "propriétés d’extension".
Attention, cependant, il faut bien que vous compreniez que cette propriété ne se trouvera pas à l’intérieur de la classe étendue, et ne pourra donc pas "stocker" de valeur en soi dans une instance (objet) de la classe.
Voyez plutôt les propriétés d’extension comme un moyen de récupérer OU/ET modifier un élément de la classe que vous souhaitez étendre, en une seule déclaration. D’ailleurs, une propriété d’extension pourra très bien être remplacée par deux fonctions d’extension (une fonction pour la récupération et une autre fonction pour la modification).
![Propriété d'extension en Kotlin](https://user.oc-static.com/upload/2023/02/06/16756737268172_16553892767247_p3c2-2.png)
Dans cet exemple, nous avons créé une propriété d’extension à la classe StringBuilder
, que nous avons appelée firstLetter
. Le but de celle-ci est de récupérer ET de modifier la première lettre d’une String
créée via la classe StringBuilder
.
Nous utilisons pour cela les mots-clés get
et set
, de la même manière que nous avons pu le faire dans un précédent chapitre.
Sachez cependant que les propriétés d’extension n’ont pas de "Backing Field" : vous ne pourrez donc pas utiliser le mot-clé field
dans leur getter et/ou leur setter ! (Et cela est normal, car la propriété n’existe en réalité pas dans la classe étendue.)
D’accord ! Mais est-ce que je pourrai les utiliser également en Java ?
Bien sûr ! Tout comme les fonctions d’extension, les propriétés d’extension ne sont en réalité que des méthodes statiques publiques que nous pourrons également appeler en Java de cette manière :
![Propriété d'extension appelée en Java](https://user.oc-static.com/upload/2023/02/06/16756738654276_16553894049753_p3c2-bis.png)
Le compilateur Kotlin génère donc automatiquement deux méthodes statiques (un getter et un setter) pour la propriété d’extension que nous venons de créer. Plutôt pratique, non ? :)
Practice Makes Perfect!
La théorie, c'est bien, mais la pratique, c'est encore mieux ! Justement, nous vous avons concocté un petit exercice interactif de fin de chapitre pour appliquer toutes ces nouvelles connaissances.
🎓Un petit exercice, ça vous tente ? C'est par ici que ça se passe.
🎁Déjà terminé ? Le corrigé se trouve juste ici.
En résumé
En Kotlin, les extensions permettent d’ajouter des fonctions ou des propriétés à une classe, qui seront perçues comme "appartenant" à la classe elle-même.
Une extension se crée dans un fichier à part, sans toucher au code source de la classe à étendre.
Pour aller plus loin
KotlinLang : Extensions