extensionsfunktion — Kotlin-mekanism som gör det möjligt att lägga till nya metoder till befintliga klasser utan arv och utan att ändra källkoden. Funktionen deklareras med ett prefix i form av en mottagartyp och anropas som en vanlig metod av denna typ. Enligt Kotlin Documentation, 2025 kompileras extensionsfunktioner till statiska metoder med den första mottagarparametern, vilket garanterar noll overhead vid körning jämfört med vanliga anrop.
Huvudpunkter
En extensionsfunktion är en funktion som deklareras utanför klassen men anropas som dess metod. Den tar emot en mottagartyp till vilken en ny metod läggs till. Inuti funktionen hänvisar nyckelordet this till instansen av mottagartypen. Detta tillvägagångssätt gör det möjligt att utöka finala klasser, tredjepartsbiblioteksklasser och till och med primitiva typer utan att skapa omslag.
Till skillnad från Java, där det för att lägga till en metod till en befintlig klass krävs antingen arv eller en statisk hjälpklass, erbjuder Kotlin en elegant lösning utan boilerplate. Enligt data från Kotlin Foundation (2024)-undersökningen tillhör extensionsfunktioner de fem mest använda språkfunktionerna — 78 % av Kotlin-utvecklarna använder dem i det dagliga arbetet.
Kotlin-kompilatorn översätter extensionsfunktionen till en statisk metod, där mottagarinstansen skickas som första argument. Detta innebär att extensionsfunktioner inte ändrar målklassens bytekod och inte bryter inkapslingen — endast mottagarens publika fält och metoder är tillgängliga.
Använd extensionsfunktioner för att skriva rena hjälpfunktioner som logiskt är bundna till en specifik typ men inte kan läggas till i dess källkod.
Grundläggande syntax för extensionsfunktion: namn på mottagartyp, punkt, funktionsnamn, sedan parametrar och returtyp. Inuti funktionen är mottagaren tillgänglig via this.
// Extensionsfunktion för String
fun String.isEmail(): Boolean {
return this.contains("@") && this.contains(".")
}
// Anropas som vanlig String-metod
val result = "user@example.com".isEmail() // true
I detta exempel blir isEmail() tillgänglig för alla strängar. Inuti funktionen är this själva strängen på vilken metoden anropas. Kotlin tillåter att this utelämnas i de flesta fall — man kan helt enkelt skriva contains("@") istället för this.contains("@").
Extensionsfunktioner kan ta emot ytterligare parametrar och returnera värden av vilken typ som helst. Detta gör dem till fullvärdiga funktioner, inte bara syntaktiskt socker.
fun List<Int>.defaultIfEmpty(default: Int): List<Int> {
if (this.isEmpty()) return listOf(default)
return this
}
val populated = listOf(1, 2).defaultIfEmpty(0)
val empty = listOf<Int>().defaultIfEmpty(0)
Parametern default bestämmer värdet som returneras för en tom lista. En extensionsfunktion kan vara generisk — typen List
Extensionsfunktioner i Kotlin använder statisk sändning, inte virtuell. Detta är den viktigaste skillnaden från vanliga klassmetoder. Vilken extensionsfunktion som kommer att anropas bestäms i kompileringsfasen baserat på variabelns statiska typ, inte dess faktiska typ vid körning.
open class Animal
class Dog : Animal()
fun Animal.speak() = "Djurläte"
fun Dog.speak() = "Skäll"
fun test() {
val animal: Animal = Dog()
println(animal.speak()) // "Djurläte" — statisk typ Animal
val dog: Dog = Dog()
println(dog.speak()) // "Skäll" — statisk typ Dog
}
Trots att variabeln animal pekar på en Dog-instans anropas extensionsfunktionen för Animal, eftersom variabelns statiska typ är Animal. Om speak() var en virtuell klassmetod skulle Dog.speak() ha anropats. Denna egenskap är viktig att ta hänsyn till vid design av API:er baserade på extensionsfunktioner.
Extensionsfunktioner är inte klassmedlemmar — de är vanliga funktioner som kräver import för att användas i en annan fil. Kotlin erbjuder två importvarianter: efter funktionsnamn eller med omdöpning.
// Import efter namn
import com.example.extensions.isEmail
// Import med alias (för att lösa konflikter)
import com.example.extensions.isEmail as isValidEmail
// Anrop efter import
val valid = "test@test.com".isEmail()
Omdöpning via as är användbar vid namnkonflikter — till exempel om två bibliotek tillhandahåller en extensionsfunktion med samma namn för samma typ. I detta fall kan en av dem importeras under ett annat namn och anropas via det nya namnet.
Extensionsfunktioner kan deklareras på olika nivåer:
| Nivå | Synlighet | Exempel |
|---|---|---|
| Top-level | Hela projektet efter import | fun String.isEmail() |
| Member extension | Inuti ägarklassen | class A { fun B.ext() } |
| Local | Inuti funktionen | fun test() { fun String.ext() } |
Member extension functions — speciellt fall när extensionsfunktionen deklareras inuti en annan klass. I detta fall är både mottagaren (funktionens this) och medlemmarna i den yttre klassen tillgängliga inuti extensionsfunktionen.
Om klassen har en metod med samma signatur som extensionsfunktionen, anropas alltid klassmedlemmen. En extensionsfunktion åsidosätter aldrig klassmetoden — detta är ett arkitektoniskt beslut för att förhindra oavsiktliga åsidosättanden.
class User {
fun greet() = "Hej från klassen"
}
fun User.greet() = "Hello from extension"
fun main() {
val user = User()
println(user.greet()) // "Hej från klassen"
}
Även om extensionsfunktionen definieras senare och har samma signatur, väljer kompilatorn klassmetoden. Kotlin-kompilatorn utfärdar en varning när en sådan situation upptäcks. Det enda sättet att anropa en extensionsfunktion i närvaro av en klassmetod är att hänvisa till den som en vanlig funktion: greet(user).
I Android-utveckling har extensionsfunktioner blivit ett standardverktyg för att arbeta med View, Context och fragment. Android KTX-biblioteket är byggt på extensionsfunktioner och tillhandahåller bekväma omslag för Android API:et.
// Extensionsfunktion för arbete med View
fun View.show() {
visibility = View.VISIBLE
}
fun View.hide() {
visibility = View.GONE
}
// Extensionsfunktion för Context — toast
fun Context.toast(message: String) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
}
// Extensionsfunktion som DSL-byggare
fun String.colorFormat(color: String): String =
"$color$this\u001b[0m"
Extensionsfunktionen toast() gör Context-beroende anrop koncisa: istället för att skriva Toast.makeText(context, message, length).show() räcker det att skriva context.toast("Text"). Detta minskar boilerplate och gör koden mer läsbar.
Vanliga frågor
Nej, extensionsfunktioner använder statisk sändning. Om du deklarerar en extensionsfunktion för basklassen och samma för underklassen, vilken som anropas bestäms av variabelns statiska typ i kompileringsfasen, inte av den faktiska typen vid körning.
Man kan deklarera extensionsfunktioner för nullable mottagartyper: fun String?.isNullOrEmail(). Inuti en sådan funktion kan this vara null, därför måste man använda säkra anropsoperatorer eller explicit null-kontroll.
Nej, extensionsfunktioner kompileras till statiska metoder. På bytekodnivå är anropet av en extensionsfunktion identiskt med anropet av en statisk metod med den första mottagarparametern. Ingen reflektion eller dynamisk sändning.
Ja, extensionsegenskaper fungerar liknande extensionsfunktioner men kan inte lagra tillstånd — endast getter och setter. Till exempel: val List<Int>.sum get() = reduce(Int::plus). De kompileras också till statiska getter/setter-metoder.
I Java skriver du StringUtils.isEmail(str), i Kotlin — str.isEmail(). Skillnaden är inte bara i syntax: extensionsfunktioner stöder autokomplettering i IDE, förbättrar läsbarheten av anropskedjor och tillåter IDE att föreslå relevanta funktioner för en specifik typ. I Java finns sådana möjligheter inte.
Sammanfattning
Vi utvecklar en mobil applikation nyckelfärdigt
IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.
Läs också