Les objets compagnons de Kotlin

S'inspirant d'autres langages de programmation, Kotlin a emprunté à Scala la notion d'objets companions ; dans le langage Kotlin, un objet compagnon se déclare dans une classe Kotlin comme un objet marqué du mot-clé companion.

La déclaration d'un objet compagnon est un cas particulier de déclaration d'un objet ; celle-ci utilise le mot-clé object, et permet de définir en fait un singleton, selon le modèle de conception de même nom.
Kotlin prend donc en charge cette notion au niveau du langage, ce qui évite de l'implémenter soi-même comme on devrait le faire avec Java par exemple.

Un objet qui est à la fois une classe et un singleton, peut tout à fait être déclaré dans une classe, et avoir accès à ses membres privés, comme dans l'exemple de classe suivant :

1
2
3
4
5
6
7
8
class Person private constructor(val name: String) {

    object Helper {
        fun createPerson(name: String): Person {
            return Person(name)
        }
    }
}

L'objet nommé Helper utilise ici le constructeur privé de la classe, et peut être référencé au travers de la classe ; la méthode createPerson() s'invoquerait par exemple ainsi :

1
2
3
4
5
Person.Helper.createPerson("odelia")

// Autre exemple
val helper = Person.Helper
helper.createPerson("alice")

Si l'on ajoute le mot-clé companion devant le mot-clé objet, on obtient un objet compagnon, et l'on pourra accéder aux membres de l'objet comme s'ils faisaient partie de la classe.
Dans ce nouvel exemple (inspiré de celui que l'on trouve dans Programming Kotlin), vous pouvez constater que la méthode create() de l'objet compagnon peut être appelé de manière transparente au travers de la classe englobante Student, en ligne 15 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
interface StudentFactory {
    fun create(name: String): Student
}

class Student private constructor(val name: String) {

    companion object Factory: StudentFactory {
        override fun create(name: String): Student {
            return Student(name)
        }
    }
}

fun main(args: Array<String>) {
    Student.create("odelia")
    Student.Factory.create("odelia")
}

A vrai dire, il est même autorisé d'omettre le nom de l'objet compagnon, et si l'on veut référencer l'objet compagnon lui-même, on pourra utiliser le nom Companion (ligne 16) :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
interface StudentFactory {
    fun create(name: String): Student
}

class Student private constructor(val name: String) {

    companion object : StudentFactory {
        override fun create(name: String): Student {
            return Student(name)
        }
    }
}

fun main(args: Array<String>) {
    Student.create("odelia")
    Student.Companion.create("odelia")
}

Permettant en Java de définir des membres de classe statiques, le mot-clé static n'existe pas dans le langage Kotlin ; c'est donc par l'intermédiaire d'un objet compagnon que l'on peut retrouver l'équivalent (mais aussi avec l'utilisation de fonctions de niveau package).

Toutefois, comme on l'aura remarqué dans les exemples donnés, un objet Kotlin peut étendre une classe ou implémenter une interface, ce qui en fait un outil plus intéressant !