Les fonctions d'extension de Kotlin

De manière similaire au langage C#, Kotlin fournit la possibilité d'étendre une classe existante avec vos propres méthodes, par le mécanisme des fonctions d'extension.
Une fonction d'extension est une fonction que l'on définit en dehors d'une classe, mais qui va pouvoir être appelée comme étant une méthode de cette classe.

Notez que les méthodes d'extension sont résolues à l'exécution.

La documentation de référence du langage Kotlin, dans le paragraphe Extension Functions, donne l'exemple de la fonction d'extension swap() permettant d'interchanger deux éléments d'une liste modifiable d'entiers, et que l'on utilise dans l'exemple suivant :

1
2
3
4
5
6
7
8
9
10
11
// Fonction d'extension sur MutableList<Int>
fun MutableList<Int>.swap(index1: Int, index2: Int) {
    val tmp = this[index1]
    this[index1] = this[index2]
    this[index2] = tmp
}

fun main(args: Array<String>) {
    val l = mutableListOf(1, 2, 3)
    l.swap(0, 2)
}

La définition de la fonction d'extension utilise une syntaxe assez claire : c'est comme si on définissait une fonction normale, mais dont le nom est préfixée avec le type (appelé le type receveur) que l'on souhaite étendre.
Et dans l'implémentation de la fonction, this désigne l'objet (c'est l'objet receveur) sur lequel la fonction est appelée.
En ligne 8, on crée une liste modifiable de trois entiers puis, en ligne 9, on invoque la fonction d'extension swap() qui se présente comme une méthode existante du type MutableList<Int> !

Fonctions d'extension en C#

A titre de comparaison, notre fonction d'extension s'écrirait en C# de la manière suivante, en passant par la déclaration d'une méthode static, dans une classe static :

1
2
3
4
5
6
7
8
9
10
// C#
public static class MyExtensions
{
    public static void Swap(this List<int> l, int index1, int index2)
    {
        int tmp = l[index1];
        l[index1] = l[index2];
        l[index2] = tmp;
    }
}

On peut d'ailleurs relever que la méthode Swap() comprend trois paramètres, le premier paramètre indiquant le type du receveur.
Ainsi, l'extension Swap() pourra s'appeler ainsi :

1
2
3
4
// C#
var l = new List<int>() { 1, 2, 3 };
l.Swap(0, 2);
l.ForEach(e => Console.WriteLine(e));

Les méthodes d'extension de Groovy

Le langage Groovy qui est également un langage de la JVM à la fois dynamique et typé, propose un mécanisme similaire avec les méthodes d'extension.

D'ailleurs, la GDK rend la JDK plus "groovy" en proposant d'emblée un grand nombre de méthodes d'extension ; on y retrouve même la méthode swap() sur différents types, dont le type Java java.util.List.
Il est même possible d'écrire ses propres modules d'extension.

Groovy permet également de définir des méthodes d'extension à l'exécution, en utilisant la meta class du type cible, ou la meta class d'un objet précis (seul l'objet est étendu).

Voici comment on pourrait définir la méthode swap() sur le type List :

1
2
3
4
5
6
7
8
9
// Groovy
List.metaClass.swap { int index1, int index2 ->
    def tmp = delegate[index1]
    delegate[index1] = delegate[index2]
    delegate[index2] = tmp
}
 
def l = [1, 2, 3]
l.swap(0, 2)