Zoeken…


Syntaxis

  • leuk TypeName.extensionName (params, ...) {/ * body * /} // Declaration
  • leuk <T: Any> TypeNameWithGenerics <T> .extensionName (params, ...) {/ * body * /} // Declaration with Generics
  • myObj.extensionName (args, ...) // aanroep

Opmerkingen

Extensies worden statisch opgelost. Dit betekent dat de te gebruiken uitbreidingsmethode wordt bepaald door het referentietype van de variabele waartoe u toegang hebt; het maakt niet uit wat het type van de variabele is tijdens runtime, dezelfde uitbreidingsmethode wordt altijd aangeroepen. Dit komt omdat het declareren van een uitbreidingsmethode geen lid toevoegt aan het type ontvanger .

Extensies op het hoogste niveau

Uitbreidingsmethoden op het hoogste niveau zijn niet opgenomen in een klasse.

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

Hierboven is een uitbreidingsmethode gedefinieerd voor het type IntArray . Merk op dat het object waarvoor de uitbreidingsmethode is gedefinieerd (de ontvanger genoemd ) wordt geopend met het trefwoord this .

Deze extensie kan zo worden genoemd:

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

Potentiële valkuil: extensies worden statisch opgelost

De uitbreidingsmethode die moet worden aangeroepen, wordt tijdens het compileren bepaald op basis van het referentietype van de variabele die wordt gebruikt. Het maakt niet uit wat het type van de variabele is tijdens runtime, dezelfde uitbreidingsmethode wordt altijd aangeroepen.

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

In het bovenstaande voorbeeld wordt "Defined for Super" afgedrukt, omdat het gedeclareerde type van de variabele myVar Super .

Voorbeeld dat zich lang uitstrekt om een voor mensen leesbare string te maken

Gegeven elke waarde van het type Int of Long om een voor mensen leesbare string te maken:

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

Dan gemakkelijk gebruikt als:

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

Voorbeeld van uitbreiding van Java 7+ Path-klasse

Een veelvoorkomende use case voor uitbreidingsmethoden is het verbeteren van een bestaande API. Hier zijn voorbeelden van het toevoegen van exist , notExists en deleteRecursively aan de Java 7+ Path klasse:

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

Die nu in dit voorbeeld kan worden opgeroepen:

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

Extensiefuncties gebruiken om de leesbaarheid te verbeteren

In Kotlin kun je code schrijven zoals:

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

Maar het gebruik van apply is niet zo duidelijk over uw intentie. Soms is het duidelijker om een soortgelijke uitbreidingsfunctie te maken om de actie in feite te hernoemen en deze voor de hand liggend te maken. Dit mag niet uit de hand lopen, maar voor veel voorkomende acties zoals verificatie:

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
}

U kunt de code nu schrijven als:

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

Dat laat mensen nu weten wat ze kunnen verwachten binnen de lambda-parameter.

Merk op dat de parameter type T voor verifiedBy hetzelfde is als T: Any? wat betekent dat zelfs nulbare typen die versie van de extensie kunnen gebruiken. Hoewel verifiedWith niet-nullable vereist.

Voorbeeld van uitbreiding van Java 8 tijdelijke klassen om een ISO-opgemaakte string te maken

Met deze verklaring:

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

U kunt nu eenvoudig:

val dateAsString = someInstant.toIsoString()

Uitbreidingsfuncties voor begeleidende objecten (weergave van statische functies)

Als u een klasse wilt uitbreiden alsof u een statische functie bent, bijvoorbeeld voor klasse Something add statical looking function fromString , kan dit alleen werken als de klasse een bijbehorend object heeft en dat de uitbreidingsfunctie op het bijbehorende object is aangegeven :

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
}

Lazy uitbreiding eigenschap oplossing

Stel dat u een extensie-eigenschap wilt maken die duur is om te berekenen. Daarom wilt u de berekening in de cache opslaan met behulp van de luie afgevaardigde en verwijzen naar de huidige instantie ( this ), maar u kunt het niet doen, zoals uitgelegd in de Kotlin-uitgaven KT-9686 en KT-13053 . Echter, er is een officiële workaround hier verstrekt .

In het voorbeeld is de extensie-eigenschap color . Het maakt gebruik van een expliciete colorCache die gebruikt kan worden met this omdat er geen lazy is noodzakelijk:

class KColor(val value: Int)

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

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

Uitbreidingen voor eenvoudiger referentie Bekijken vanuit code

U kunt extensies gebruiken voor referentie View, geen boilerplate meer nadat u de views hebt gemaakt.

Original Idea is van Anko Library

uitbreidingen

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

Gebruik

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
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow