Les propriétés d'extension sont des extensions pour des classes qui permettent d'ajouter des accesseurs et des mutateurs sans possibilité d'ajouter un état (backing field). En Java, cette possibilité n'existe pas, et l'analogue est l'écriture de méthodes utilitaires. Historiquement, pour ajouter des fonctionnalités, il a fallu utiliser des méthodes statiques ou des objets Wrapper.
Problème : dans les classes tierces, il manque souvent les propriétés nécessaires. On veut étendre la classe de manière concise et sécurisée, sans enfreindre l'encapsulation.
Solution : en Kotlin, on peut déclarer une propriété d'extension, qui ressemble à une propriété ordinaire, mais est réalisée sous forme de fonctions. Cela permet d'étendre des types pour lesquels nous n'avons pas accès au code source, de manière pratique.
Exemple de code :
val String.firstChar: Char get() = this[0] println("Kotlin".firstChar) // K
Caractéristiques clés :
Peut-on ajouter un backing field (état) dans une propriété d'extension ?
Non, une propriété d'extension ne fait que calculer la valeur à la volée et ne peut pas stocker d'état.
Les propriétés d'extension peuvent-elles redéfinir des propriétés dans des classes héritées ?
Non, les propriétés d'extension (comme les fonctions d'extension) sont intrinsèquement statiques : elles ne peuvent pas être redéfinies ou être virtuelles.
Comment une propriété d'extension est-elle compilée, et pourquoi n'est-elle pas un analogue syntaxique d'une propriété ordinaire ?
Une propriété d'extension est en réalité compilée en un accesseur (et/ou mutateur) statique. Elles ne font pas partie des entités de la classe et ne sont visibles que dans le contexte du fichier où elles sont déclarées.
Cas négatif
Un développeur a essayé d'ajouter une propriété d'extension pour stocker l'état "visité" dans les View standard d'Android :
var View.isVisited: Boolean get() = ... set(value) { ... } // Erreur : pas de stockage
Avantages :
Inconvénients :
Cas positif
Une propriété d'extension a été réalisée pour String afin d'obtenir un format étendu :
val String.asTitle: String get() = this.replaceFirstChar { it.uppercase() }
Avantages :
Inconvénients :