Suche…


Syntax

  • fun TypeName.extensionName (Parameter, ...) {/ * body * /} // Deklaration
  • fun <T: Any> TypeNameWithGenerics <T> .extensionName (params, ...) {/ * body * /} // Deklaration mit Generics
  • myObj.extensionName (args, ...) // Aufruf

Bemerkungen

Erweiterungen werden statisch aufgelöst. Dies bedeutet, dass die zu verwendende Erweiterungsmethode durch den Referenztyp der Variablen bestimmt wird, auf die Sie zugreifen. Es spielt keine Rolle, welchen Typ die Variable zur Laufzeit hat, es wird immer dieselbe Erweiterungsmethode aufgerufen. Dies liegt daran, dass beim Deklarieren einer Erweiterungsmethode dem Empfängertyp kein Mitglied tatsächlich hinzugefügt wird .

Erweiterungen der obersten Ebene

Erweiterungsmethoden der obersten Ebene sind nicht in einer Klasse enthalten.

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

Oben ist eine Erweiterungsmethode für den Typ IntArray . Beachten Sie, dass auf das Objekt, für das die Erweiterungsmethode definiert ist (als Empfänger bezeichnet ), mit dem Schlüsselwort this zugegriffen wird.

Diese Erweiterung kann wie folgt aufgerufen werden:

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

Mögliche Gefahr: Erweiterungen werden statisch aufgelöst

Die aufzurufende Erweiterungsmethode wird zur Kompilierzeit basierend auf dem Referenztyp der Variablen bestimmt, auf die zugegriffen wird. Es spielt keine Rolle, was der Variablentyp zur Laufzeit ist, es wird immer dieselbe Erweiterungsmethode aufgerufen.

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

Im obigen Beispiel wird "Defined for Super" gedruckt, da der deklarierte Typ der Variablen myVar Super .

Beispiel, das sich lang ausdehnt, um eine für Menschen lesbare Zeichenfolge darzustellen

Geben Sie einen beliebigen Wert vom Typ Int oder Long , um eine für Menschen lesbare Zeichenfolge darzustellen:

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

Dann einfach als:

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

Beispiel für die Erweiterung der Klasse Java 7+ Path

Ein üblicher Anwendungsfall für Erweiterungsmethoden ist die Verbesserung einer vorhandenen API. Hier einige Beispiele für das Hinzufügen von exist , notExists und deleteRecursively zur Java 7+ Path Klasse:

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

Was kann nun in diesem Beispiel aufgerufen werden:

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

Erweiterungsfunktionen verwenden, um die Lesbarkeit zu verbessern

In Kotlin könnte man Code schreiben wie:

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

Die Verwendung von apply ist jedoch nicht so klar wie Ihre Absicht. Manchmal ist es klarer, eine ähnliche Erweiterungsfunktion zu erstellen, um die Aktion tatsächlich umzubenennen und sie selbstverständlich zu machen. Dies sollte nicht außer Kontrolle geraten, sondern bei sehr häufigen Aktionen wie der Verifizierung:

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
}

Sie könnten jetzt den Code schreiben als:

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

Was nun die Leute wissen lässt, was innerhalb des Lambda-Parameters zu erwarten ist.

Beachten Sie, dass der Typparameter T für verifiedBy mit T: Any? Das bedeutet, dass auch nullfähige Typen diese Version der Erweiterung verwenden können. Obwohl verifiedWith nicht nullfähig ist.

Beispiel zur Erweiterung von Java 8 Temporal-Klassen zum Rendern einer ISO-formatierten Zeichenfolge

Mit dieser Erklärung:

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

Sie können jetzt einfach:

val dateAsString = someInstant.toIsoString()

Erweiterungsfunktionen für Begleitobjekte (Darstellung statischer Funktionen)

Wenn Sie eine Klasse als statische Funktion erweitern möchten, z. B. für die Klasse Something add statisch fromString Funktion, kann dies nur funktionieren, wenn die Klasse über ein Begleitobjekt verfügt und die Erweiterungsfunktion für das Begleitobjekt deklariert wurde :

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
}

Problemumgehung für faul Erweiterungs-Eigenschaften

Angenommen, Sie möchten eine Erweiterungseigenschaft erstellen, deren Berechnung teuer ist. Daher möchten Sie die Berechnung zwischenspeichern, indem Sie den verzögerten Eigenschaftsdelegierten verwenden und auf die aktuelle Instanz ( this ) verweisen. this ist jedoch nicht möglich, wie in den Kotlin-Ausgaben KT-9686 und KT-13053 erläutert. Es wird jedoch eine offizielle Problemumgehung bereitgestellt .

Im Beispiel ist die Erweiterungseigenschaft color . Es verwendet eine explizite colorCache , die mit verwendet werden kann this als nicht lazy ist notwendig:

class KColor(val value: Int)

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

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

Erweiterungen zum leichteren Nachschlagen View from code

Sie können Erweiterungen für die Referenzansicht verwenden, keine Boilerplate mehr, nachdem Sie die Ansichten erstellt haben.

Ursprüngliche Idee stammt von Anko Library

Erweiterungen

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

Verwendungszweck

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
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow