Recherche…


Syntaxe

  • fun TypeName.extensionName (params, ...) {/ * body * /} // Déclaration
  • fun <T: Any> TypeNameWithGenerics <T> .extensionName (params, ...) {/ * body * /} // Déclaration avec des génériques
  • myObj.extensionName (args, ...) // invocation

Remarques

Les extensions sont résolues de manière statique . Cela signifie que la méthode d’extension à utiliser est déterminée par le type de référence de la variable à laquelle vous accédez; Peu importe le type de la variable à l'exécution, la même méthode d'extension sera toujours appelée. En effet, la déclaration d’une méthode d’extension n’ajoute pas réellement un membre au type de récepteur .

Extensions de niveau supérieur

Les méthodes d'extension de niveau supérieur ne sont pas contenues dans une classe.

fun IntArray.addTo(dest: IntArray) {
    for (i in 0 .. size - 1) {
        dest[i] += this[i]
    }
}

Au-dessus d'une méthode d'extension est défini pour le type IntArray . Notez que l'objet pour lequel la méthode d'extension est définie (appelé le récepteur ) est accessible à l'aide du mot this clé this .

Cette extension peut être appelée comme ceci:

val myArray = intArrayOf(1, 2, 3)
intArrayOf(4, 5, 6).addTo(myArray)

Piège potentiel: les extensions sont résolues de manière statique

La méthode d'extension à appeler est déterminée au moment de la compilation en fonction du type de référence de la variable à laquelle on accède. Quel que soit le type de la variable à l'exécution, la même méthode d'extension sera toujours appelée.

open class Super

class Sub : Super()

fun Super.myExtension() = "Defined for Super"

fun Sub.myExtension() = "Defined for Sub"

fun callMyExtension(myVar: Super) {
    println(myVar.myExtension())
}

callMyExtension(Sub())

L'exemple ci-dessus affichera "Defined for Super" , car le type déclaré de la variable myVar est Super .

Echantillon s'étendant longtemps pour rendre une chaîne lisible par l'homme

Étant donné toute valeur de type Int ou Long pour rendre une chaîne lisible par un humain:

fun Long.humanReadable(): String {
    if (this <= 0) return "0"
    val units = arrayOf("B", "KB", "MB", "GB", "TB", "EB")
    val digitGroups = (Math.log10(this.toDouble())/Math.log10(1024.0)).toInt();
    return DecimalFormat("#,##0.#").format(this/Math.pow(1024.0, digitGroups.toDouble())) + " " + units[digitGroups];
}

fun Int.humanReadable(): String {
    return this.toLong().humanReadable()
}

Puis facilement utilisé comme:

println(1999549L.humanReadable())
println(someInt.humanReadable())

Exemple d'extension de la classe Java 7+ Path

Un cas d'utilisation courant des méthodes d'extension consiste à améliorer une API existante. Voici des exemples d'ajout de exist , notExists et deleteRecursively à la classe Java 7+ Path :

fun Path.exists(): Boolean = Files.exists(this)
fun Path.notExists(): Boolean = !this.exists()
fun Path.deleteRecursively(): Boolean = this.toFile().deleteRecursively()

Qui peut maintenant être invoqué dans cet exemple:

val dir = Paths.get(dirName)
if (dir.exists()) dir.deleteRecursively()

Utilisation des fonctions d'extension pour améliorer la lisibilité

Dans Kotlin, vous pouvez écrire du code comme:

val x: Path = Paths.get("dirName").apply { 
    if (Files.notExists(this)) throw IllegalStateException("The important file does not exist")
}

Mais l'utilisation de apply n'est pas si claire quant à votre intention. Parfois, il est plus clair de créer une fonction d'extension similaire pour renommer l'action et la rendre plus évidente. Cela ne devrait pas être autorisé à devenir incontrôlable, mais pour des actions très courantes telles que la vérification:

infix inline fun <T> T.verifiedBy(verifyWith: (T) -> Unit): T {
    verifyWith(this)
    return this
}

infix inline fun <T: Any> T.verifiedWith(verifyWith: T.() -> Unit): T {
    this.verifyWith()
    return this
}

Vous pouvez maintenant écrire le code en tant que:

val x: Path = Paths.get("dirName") verifiedWith {
    if (Files.notExists(this)) throw IllegalStateException("The important file does not exist")
}

Ce qui permet maintenant aux gens de savoir à quoi s’attendre dans le paramètre lambda.

Notez que le paramètre de type T pour verifiedBy est le même que T: Any? ce qui signifie que même les types nullables pourront utiliser cette version de l'extension. Bien que verifiedWith nécessite non-nullable.

Exemple d'extension de classes temporelles Java 8 pour le rendu d'une chaîne au format ISO

Avec cette déclaration:

fun Temporal.toIsoString(): String = DateTimeFormatter.ISO_INSTANT.format(this)

Vous pouvez maintenant simplement:

val dateAsString = someInstant.toIsoString()

Fonctions d'extension aux objets compagnons (apparition de fonctions statiques)

Si vous souhaitez étendre une classe, si vous êtes une fonction statique, par exemple pour la classe Something fonction statique ajouter à la recherche fromString , cela ne peut fonctionner que si la classe a un objet compagnon et que la fonction d'extension a été déclarée sur l'objet compagnon :

class Something {
    companion object {}
}

class SomethingElse {
}

fun Something.Companion.fromString(s: String): Something = ... 

fun SomethingElse.fromString(s: String): SomethingElse = ... 

fun main(args: Array<String>) {
    Something.fromString("") //valid as extension function declared upon the
                             //companion object

    SomethingElse().fromString("") //valid, function invoked on instance not
                                   //statically

    SomethingElse.fromString("") //invalid
}

Solution de contournement de propriété d'extension paresseuse

Supposons que vous souhaitiez créer une propriété d'extension coûteuse à calculer. Vous souhaitez donc mettre le calcul en cache en utilisant le délégué de propriété paresseux et faire référence à l'instance actuelle ( this ), mais vous ne pouvez pas le faire, comme expliqué dans les problèmes Kotlin KT-9686 et KT-13053 . Cependant, il existe une solution de contournement officielle fournie ici .

Dans l'exemple, la propriété d'extension est color . Il utilise un colorCache explicite qui peut être utilisé avec this car aucun lazy n'est nécessaire:

class KColor(val value: Int)

private val colorCache = mutableMapOf<KColor, Color>()

val KColor.color: Color
    get() = colorCache.getOrPut(this) { Color(value, true) }

Extensions pour une référence plus facile Voir du code

Vous pouvez utiliser des extensions pour la vue de référence, plus de passe-partout après avoir créé les vues.

Idée originale de la bibliothèque Anko

Les extensions

inline fun <reified T : View> View.find(id: Int): T = findViewById(id) as T
inline fun <reified T : View> Activity.find(id: Int): T = findViewById(id) as T
inline fun <reified T : View> Fragment.find(id: Int): T = view?.findViewById(id) as T
inline fun <reified T : View> RecyclerView.ViewHolder.find(id: Int): T = itemView?.findViewById(id) as T

inline fun <reified T : View> View.findOptional(id: Int): T? = findViewById(id) as? T
inline fun <reified T : View> Activity.findOptional(id: Int): T? = findViewById(id) as? T
inline fun <reified T : View> Fragment.findOptional(id: Int): T? = view?.findViewById(id) as? T
inline fun <reified T : View> RecyclerView.ViewHolder.findOptional(id: Int): T? = itemView?.findViewById(id) as? T

Usage

val yourButton by lazy { find<Button>(R.id.yourButtonId) }
val yourText by lazy { find<TextView>(R.id.yourTextId) }
val yourEdittextOptional by lazy { findOptional<EditText>(R.id.yourOptionEdittextId) }


Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow