Objet compagnon en Kotlin : remplacement de Java static et performances

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Pourquoi Kotlin n'a-t-il pas de static, mais un objet compagnon ? Comprendre les performances et la logique



Si vous passez de Java à Kotlin, l'une des premières questions que vous poserez sur StackOverflow (et elle a effectivement recueilli des milliers de vues) est : « Pourquoi utilisons-nous un objet compagnon au lieu de Java static ? Et cela ne ralentit-il pas l'application ? »



Soyons honnêtes : quand j'ai vu pour la première fois la construction companion object, j'ai eu l'impression que l'équipe Kotlin avait simplement décidé de compliquer la vie. Pourquoi écrire plus de code alors qu'il suffisait de copier static de Java ? Mais en creusant plus profondément, j'ai compris : ce n'est pas un caprice, mais une décision architecturale réfléchie qui rend le code plus propre, plus sûr et… oui, parfois même plus rapide.



Dans cet article, nous ne nous contenterons pas de traduire une réponse de SO. Nous analyserons comment fonctionne un objet compagnon sous le capot, comparerons ses performances avec Java static et montrerons quand l'utiliser, et quand il est préférable de choisir une autre voie.



Qu'est-ce qu'un objet compagnon en termes simples ?



En Java, les champs et méthodes statiques appartiennent à la classe, et non à l'instance. Kotlin a décidé de s'éloigner du mot-clé static car il viole les principes de la POO (le statique ne peut pas être redéfini, il ne fait pas partie de l'objet). À la place, Kotlin propose des objets compagnons.



Imaginez que vous avez une classe User. Vous souhaitez stocker une constante MAX_AGE et une méthode factory createGuest(). En Java, vous écririez :



public class User {    public static final int MAX_AGE = 120;    public static User createGuest() {        return new User("guest", "1234");    }}


En Kotlin, cela ressemble à ceci :



class User(private val name: String, private val password: String) {    companion object {        const val MAX_AGE = 120        fun createGuest() = User("guest", "1234")    }}


Notez que companion object n'est pas simplement un « bloc static ». C'est un objet à part entière qui vit à l'intérieur de votre classe. Il peut avoir des interfaces, des extensions et même son propre nom (si vous le définissez).



Comment fonctionne un objet compagnon sous le capot ? (Du point de vue de la JVM)



Pour comprendre les performances, regardons le bytecode. Lorsque vous écrivez un objet compagnon, le compilateur Kotlin fait ce qui suit :



  • Il crée une classe statique interne nommée Companion (ou votre nom personnalisé).
  • Il place tous les champs et méthodes de l'objet compagnon dans cette classe.
  • Si vous utilisez l'annotation @JvmStatic, le compilateur génère en plus des méthodes statiques wrapper dans la classe elle-même.


Exemple avec l'annotation @JvmStatic :



class User {    companion object {        @JvmStatic        fun createGuest() = User()    }}


Désormais, depuis Java, vous pouvez appeler User.createGuest() comme une méthode statique normale. Sans cette annotation, il faudrait écrire User.Companion.createGuest().



Conclusion sur les performances : si vous travaillez uniquement avec Kotlin, il n'y a pas de surcharge supplémentaire. Si vous appelez depuis Java, il y a un niveau d'indirection supplémentaire (via l'objet Companion). Mais le compilateur JIT de la JVM inline très bien ces appels, donc la différence de vitesse tend vers zéro.



Performances : objet compagnon vs Java static



Démystifions le principal mythe : l'objet compagnon NE ralentit PAS. J'ai effectué plusieurs micro-benchmarks (sur Kotlin 1.9, JVM 17), et voici les résultats :



Blogs

Book Recommendations

Scénario Java static