ProgrammationDéveloppeur Kotlin

Comment fonctionne l'opérateur 'by' lors de la délégation d'interfaces en Kotlin ? En quoi la délégation d'une interface diffère-t-elle de la délégation d'une propriété, quels sont les avantages et les inconvénients, donnez un exemple de code.

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse.

La délégation d'une interface à l'aide de l'opérateur by permet à une classe de rediriger tous les appels d'interface vers un objet délégué spécifique. Cela réduit la duplication de code et implémente le modèle de composition.

Exemple :

interface Logger { fun log(message: String) } class ConsoleLogger: Logger { override fun log(message: String) = println("LOG: $message") } class Service(logger: Logger): Logger by logger { fun doWork() { log("Service is working") } } val service = Service(ConsoleLogger()) service.doWork() // LOG: Service is working

Différences par rapport à la délégation de propriété :

  • La délégation d'interface s'applique aux classes et à tous les membres de l'interface.
  • La délégation de propriété (val/var x by ...) s'applique à une propriété spécifique et nécessite l'implémentation de l'interface du délégué (par exemple, ReadWriteProperty).
  • La délégation d'interface offre une implémentation compacte, mais expose tout l'API de l'interface du délégué.

Avantages :

  • Simplifie considérablement l'implémentation du modèle de décorateur et de délégation.
  • Permet de modifier le comportement des interfaces standards par composition.

Inconvénients :

  • Tous les méthodes de l'interface sont toujours déléguées, il est impossible d'"intercepter" des appels individuels sans redéfinition explicite.
  • Des ambiguïtés peuvent survenir lors de l'héritage de plusieurs interfaces avec des méthodes identiques.

Question piège.

En quoi la délégation d'interface (by) diffère-t-elle de l'implémentation d'une interface en passant un objet via un champ ?

Réponse : La délégation (via by) implémente automatiquement toutes les méthodes de l'interface via l'objet délégué. Si l'on stocke simplement l'objet délégué en tant que champ et appelle ses méthodes manuellement, il est nécessaire d'écrire chaque méthode de l'interface manuellement - ce qui entraîne de la duplication et des erreurs. De plus, la délégation via by offre une meilleure lisibilité et moins de code boilerplate :

// Sans délégation class Service2(private val logger: Logger): Logger { override fun log(message: String) = logger.log(message) }

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


Histoire

Dans un projet, ils ont essayé d'implémenter le modèle de décorateur pour l'interface Logger à la main, oubliant d'implémenter une méthode supplémentaire de l'interface qui a été ajoutée plus tard à Logger. Le projet se compilait, mais la nouvelle fonctionnalité ne fonctionnait pas car l'implémentation était "vide". La délégation d'interface via by aurait permis d'éviter cette erreur : tous les nouveaux méthodes sont automatiquement implémentées par le délégué.


Histoire

Lors de la délégation d'interface via by, le développeur a redéfini l'une des méthodes de l'interface, mais a oublié que les autres méthodes passent toujours par le délégué. En conséquence, une partie de la fonctionnalité fonctionnait de manière "non standard" - l'erreur n'a pas été détectée pendant longtemps dans la logique des méthodes métier.


Histoire

Ils ont essayé de mettre en œuvre la délégation de plusieurs interfaces avec des méthodes se chevauchant via by, un conflit est survenu - le compilateur a commencé à générer une erreur d'ambiguïté, et il a été nécessaire de redéfinir explicitement les méthodes dupliquées, sinon le projet ne se compilait pas.