Kotlin
Förlängningsmetoder
Sök…
Syntax
- fun TypeName.extensionName (params, ...) {/ * body * /} // Deklaration
- fun <T: Any> TypeNameWithGenerics <T> .extensionName (params, ...) {/ * body * /} // Declaration with Generics
- myObj.extensionName (args, ...) // invocation
Anmärkningar
Tillägg löses statiskt . Detta innebär att den förlängningsmetod som ska användas bestäms av referenstypen för den variabel du har åtkomst till; det spelar ingen roll vad variabelens typ är vid körning, samma förlängningsmetod kommer alltid att kallas. Detta beror på att deklarerande av en förlängningsmetod faktiskt inte lägger till en medlem i mottagartypen .
Toppnivåförlängningar
Förlängningsmetoder på toppnivå ingår inte i en klass.
fun IntArray.addTo(dest: IntArray) {
for (i in 0 .. size - 1) {
dest[i] += this[i]
}
}
Ovanför en förlängningsmetod definieras för typen IntArray
. Observera att objektet för vilket förlängningsmetoden definieras (kallas mottagaren ) nås med hjälp av nyckelordet this
.
Denna tillägg kan kallas så:
val myArray = intArrayOf(1, 2, 3)
intArrayOf(4, 5, 6).addTo(myArray)
Potentiell fallgrop: Förlängningar löses statiskt
Den förlängningsmetod som ska kallas bestäms vid sammanställningstiden baserat på referenstypen för den variabel som åtkomst till. Det spelar ingen roll vad variabelens typ är vid körning, samma förlängningsmetod kommer alltid att kallas.
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())
Exemplet ovan kommer att skriva ut "Defined for Super"
, eftersom den deklarerade typen av variabeln myVar
är Super
.
Prov som sträcker sig långt för att göra en mänsklig läsbar sträng
Med tanke på något värde av typen Int
eller Long
att göra en mänsklig läsbar sträng:
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()
}
Sedan lätt att använda som:
println(1999549L.humanReadable())
println(someInt.humanReadable())
Exempel på utvidgning av Java 7+ sökväg
Ett vanligt användningsfall för förlängningsmetoder är att förbättra ett befintligt API. Här är exempel på att lägga till exist
, notExists
och deleteRecursively
till Java 7+ Path
Klass:
fun Path.exists(): Boolean = Files.exists(this)
fun Path.notExists(): Boolean = !this.exists()
fun Path.deleteRecursively(): Boolean = this.toFile().deleteRecursively()
Som nu kan åberopas i detta exempel:
val dir = Paths.get(dirName)
if (dir.exists()) dir.deleteRecursively()
Använda förlängningsfunktioner för att förbättra läsbarheten
I Kotlin kan du skriva kod som:
val x: Path = Paths.get("dirName").apply {
if (Files.notExists(this)) throw IllegalStateException("The important file does not exist")
}
Men användningen av apply
är inte så tydlig när det gäller din avsikt. Ibland är det tydligare att skapa en liknande förlängningsfunktion för att i själva verket byta namn på handlingen och göra den mer självklar. Detta bör inte tillåtas komma ur handen, men för mycket vanliga åtgärder som verifiering:
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
}
Du kan nu skriva koden som:
val x: Path = Paths.get("dirName") verifiedWith {
if (Files.notExists(this)) throw IllegalStateException("The important file does not exist")
}
Låt oss nu veta vad de kan förvänta sig inom lambda-parametern.
Observera att typparametern T
för verifiedBy
är samma som T: Any?
vilket betyder att även nollbara typer kommer att kunna använda den versionen av tillägget. Även om verifiedWith
kräver icke-nullable.
Exempel på utvidgning av Java 8 Temporal klasser för att göra en ISO-formaterad sträng
Med denna förklaring:
fun Temporal.toIsoString(): String = DateTimeFormatter.ISO_INSTANT.format(this)
Du kan nu helt enkelt:
val dateAsString = someInstant.toIsoString()
Förlängningsfunktioner till Companion Objects (utseende av statiska funktioner)
Om du vill utöka en klass som om du är en statisk funktion, till exempel för klassen Something
lägg till statisk utseende-funktion från fromString
, kan detta bara fungera om klassen har ett följeslagareobjekt och att förlängningsfunktionen har deklarerats på ledsagarobjektet :
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
}
Lös lösning för fastighetsförlängning
Antag att du vill skapa en tilläggsegenskap som är dyr att beräkna. Således vill du cache beräkningen genom att använda den lata egenskapens delegat och hänvisa till nuvarande instans ( this
), men du kan inte göra det, vilket förklaras i Kotlins utgåvor KT-9686 och KT-13053 . Det finns emellertid en officiell lösning här .
I exemplet är förlängningsegenskapen color
. Den använder en tydlig colorCache
som kan användas med this
eftersom det inte är lazy
:
class KColor(val value: Int)
private val colorCache = mutableMapOf<KColor, Color>()
val KColor.color: Color
get() = colorCache.getOrPut(this) { Color(value, true) }
Tillägg för enklare referens Visa från kod
Du kan använda tillägg för referensvy, inte mer pannplatta efter att du skapat vyerna.
Originalidé är av Anko Library
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
Användande
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) }