Ricerca…


Sintassi

  • fun TypeName.extensionName (params, ...) {/ * body * /} // Dichiarazione
  • fun <T: Any> TypeNameWithGenerics <T> .extensionName (params, ...) {/ * body * /} // Dichiarazione con Generics
  • myObj.extensionName (args, ...) // invocazione

Osservazioni

Le estensioni sono risolte staticamente . Ciò significa che il metodo di estensione da utilizzare è determinato dal tipo di riferimento della variabile a cui si sta accedendo; non importa quale sia il tipo della variabile in fase di esecuzione, verrà sempre chiamato lo stesso metodo di estensione. Questo perché dichiarare un metodo di estensione non aggiunge effettivamente un membro al tipo di destinatario .

Estensioni di primo livello

I metodi di estensione di primo livello non sono contenuti in una classe.

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

Sopra un metodo di estensione è definito per il tipo IntArray . Si noti che l'oggetto per cui è definito il metodo di estensione (chiamato ricevitore ) è accessibile usando la parola chiave this .

Questa estensione può essere chiamata in questo modo:

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

Potenziale trappola: le estensioni vengono risolte staticamente

Il metodo di estensione da chiamare viene determinato al momento della compilazione in base al tipo di riferimento della variabile a cui si accede. Non importa quale sia il tipo della variabile in fase di esecuzione, verrà sempre chiamato lo stesso metodo di estensione.

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'esempio precedente stamperà "Defined for Super" , perché il tipo dichiarato della variabile myVar è Super .

Esempio che si estende a lungo per rendere una stringa leggibile dall'uomo

Dato qualsiasi valore di tipo Int o Long per rendere una stringa leggibile dall'uomo:

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()
}

Quindi facilmente utilizzabile come:

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

Esempio di estensione della classe Java 7+ Path

Un caso di utilizzo comune per i metodi di estensione è il miglioramento di un'API esistente. Ecco alcuni esempi di aggiunta di exist , notExists e deleteRecursively al Java 7+ Path classe:

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

Quale ora può essere invocato in questo esempio:

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

Utilizzo delle funzioni di estensione per migliorare la leggibilità

In Kotlin puoi scrivere codice come:

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

Ma l'uso di apply non è chiaro per quanto riguarda il tuo intento. A volte è più chiaro creare una funzione di estensione simile per rinominare in effetti l'azione e renderla più evidente. Questo non dovrebbe essere permesso di sfuggire di mano, ma per azioni molto comuni come la verifica:

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
}

Ora puoi scrivere il codice come:

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

Che ora facciamo sapere alle persone cosa aspettarsi all'interno del parametro lambda.

Si noti che il parametro di tipo T per verifiedBy è uguale a T: Any? il che significa che anche i tipi nullable saranno in grado di usare quella versione dell'estensione. Sebbene verifiedWith richiede non nullable.

Esempio di estensione delle classi temporali di Java 8 per il rendering di una stringa formattata ISO

Con questa dichiarazione:

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

Ora puoi semplicemente:

val dateAsString = someInstant.toIsoString()

Funzioni di estensione agli oggetti companion (aspetto delle funzioni statiche)

Se si desidera estendere una classe come-se si è una funzione statica, ad esempio per la classe Something aggiunge la funzione di ricerca statica fromString , questo può funzionare solo se la classe ha un oggetto associato e che la funzione di estensione è stata dichiarata sull'oggetto associato :

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
}

Soluzione di proprietà Lazy extension

Si supponga di voler creare una proprietà di estensione che è costosa da calcolare. Pertanto, si desidera memorizzare nella cache il calcolo, utilizzando il delegato della proprietà lazy e fare riferimento all'istanza corrente ( this ), ma non è possibile farlo, come spiegato nei problemi Kotlin KT-9686 e KT-13053 . Tuttavia, v'è una soluzione ufficiale qui fornite .

Nell'esempio, la proprietà dell'estensione è a color . Usa un colorCache esplicito che può essere usato con this dato che non è necessario alcun lazy :

class KColor(val value: Int)

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

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

Estensioni per un più facile riferimento Visualizza dal codice

È possibile utilizzare le estensioni per la vista di riferimento, non più codice standard dopo aver creato le viste.

L'idea originale è di Anko Library

estensioni

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

uso

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
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow