ProgrammationDéveloppeur Backend

Expliquez les subtilités de l'utilisation de l'opérateur 'object' en Kotlin : qu'est-ce que les objets singleton, les expressions d'objet, les déclarations d'objet et les objets compagnons. Donnez des exemples d'utilisation et des erreurs possibles.

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse.

Kotlin étend le concept classique de singleton grâce au mot clé object. Cela permet de réaliser les patterns suivants :

  • Déclaration d'objet (object declaration) — crée une seule instance pour toute l'application (object Logger { ... }).
  • Expression d'objet (object expression) — crée un objet anonyme directement au lieu d'utilisation, par exemple pour réaliser des interfaces ou des gestionnaires d'événements.
  • Objets compagnons (companion object) — permettent de déclarer des membres statiques dans une classe.

Exemple de singleton :

object DatabaseManager { fun connect() { /*...*/ } } DatabaseManager.connect()

Expression d'objet :

val listener = object : MouseAdapter() { override fun mouseClicked(e: MouseEvent) { /*...*/ } }

Objet compagnon :

class User { companion object Factory { fun create(name: String) = User() } } val user = User.create("Ivan")

Nuances :

  • Les objets compagnons sont visibles comme des champs statiques au niveau du bytecode.
  • Les objets compagnons peuvent réaliser des interfaces.
  • L'expression d'objet n'est pas un singleton, elle est créée à chaque fois qu'on y accède.
  • La déclaration d'objet est initialisée paresseusement, lors de la première invocation.

Question piège.

Quelle est la différence entre companion object et object declaration ? Tous leurs membres sont-ils accessibles comme statiques ?

Réponse :

  • object declaration — singleton global, membre d'une classe, d'une interface ou d'un niveau externe.
  • companion object — un type spécial de déclaration d'objet à l'intérieur d'une classe, dont les membres peuvent être appelés comme s'ils étaient statiques (par le nom de la classe). Cependant, contrairement à Java, ils sont en fait des champs d'un objet singleton.

Exemple de différence :

class A { companion object { fun foo() {} } object NestedObj { fun bar() {} } } A.foo() // OK A.NestedObj.bar() // OK, mais ce n'est pas une méthode statique

Exemples d'erreurs réelles dues à l'ignorance des subtilités du sujet.


Histoire

Un développeur a défini un état mutable à l'intérieur d'une déclaration d'objet et a commencé à l'utiliser depuis différents threads sans synchronisation, ignorant que les objets singleton sont partagés dans toute l'application et peuvent causer des conditions de course.


Histoire

En déclarant un objet au lieu d'un objet compagnon à l'intérieur d'une classe, il était nécessaire d'utiliser des méthodes statiques, mais elles ont dû être appelées via une instance, ce qui a détérioré la lisibilité et a causé des erreurs lors de la migration depuis Java.


Histoire

Dans le code UI, le programmeur créait à chaque fois un nouvel objet via une expression d'objet pour le gestionnaire d'événements. Il croyait à tort qu'il s'agissait d'un singleton et que l'état serait conservé ; par conséquent, des fuites de mémoire se sont produites en raison d'une mauvaise gestion du cycle de vie.