funcția de extensie — mecanismul Kotlin care permite adăugarea de noi metode la clase existente fără moștenire și fără modificarea codului sursă. Funcția este declarată cu un prefix de tip receptor și este apelată ca o metodă obișnuită a acestui tip. Potrivit Kotlin Documentation, 2025, funcțiile de extensie sunt compilate în metode statice cu primul parametru receptor, ceea ce garantează suprasarcină zero în runtime în comparație cu apelurile obișnuite.
Principalele
Funcția de extensie este o funcție declarată în afara clasei, dar apelată ca metodă a acesteia. Ea primește un tip receptor la care se adaugă o nouă metodă. În interiorul funcției, cuvântul cheie this se referă la instanța tipului receptor. Această abordare permite extinderea claselor finale, a claselor din biblioteci terțe și chiar a tipurilor primitive fără a crea învelitori.
Spre deosebire de Java, unde pentru a adăuga o metodă la o clasă existentă este necesară fie moștenirea, fie o clasă utilitară statică, Kotlin oferă o soluție elegantă fără boilerplate. Potrivit datelor cercetării Kotlin Foundation (2024), funcțiile de extensie se află în primele cinci cele mai utilizate caracteristici ale limbajului — 78% dintre dezvoltatorii Kotlin le folosesc în munca de zi cu zi.
Compilatorul Kotlin transformă funcția de extensie într-o metodă statică, unde primul argument este instanța receptorului. Aceasta înseamnă că funcțiile de extensie nu modifică bytecodul clasei țintă și nu încalcă încapsularea — sunt accesibile doar câmpurile și metodele publice ale receptorului.
Folosiți funcțiile de extensie pentru a scrie funcții utilitare curate care sunt legate logic de un anumit tip, dar nu pot fi adăugate în codul său sursă.
Sintaxa de bază a funcției de extensie: numele tipului receptor, punct, numele funcției, apoi parametrii și tipul de returnare. În interiorul funcției, receptorul este accesibil prin this.
// Funcție de extensie pentru String
fun String.isEmail(): Boolean {
return this.contains("@") && this.contains(".")
}
// Apelată ca metodă obișnuită String
val result = "user@example.com".isEmail() // true
În acest exemplu, isEmail() devine disponibil pentru toate șirurile. În interiorul funcției, this este însuși șirul pentru care se apelează metoda. Kotlin permite omiterea lui this în majoritatea cazurilor — se poate scrie pur și simplu contains("@") în loc de this.contains("@").
Funcțiile de extensie pot primi parametri suplimentari și pot returna valori de orice tip. Acest lucru le face funcții complete, nu doar zahăr sintactic.
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)
Parametrul default determină valoarea returnată pentru o listă goală. Funcția de extensie poate fi generică — tipul List
Funcțiile de extensie în Kotlin folosesc dispecerizare statică, nu virtuală. Aceasta este diferența cheie față de metodele obișnuite ale claselor. Care funcție de extensie va fi apelată se determină în faza de compilare după tipul static al variabilei, nu după tipul ei real în runtime.
open class Animal
class Dog : Animal()
fun Animal.speak() = "Sunet animal"
fun Dog.speak() = "Ham"
fun test() {
val animal: Animal = Dog()
println(animal.speak()) // „Sunet animal” — tip static Animal
val dog: Dog = Dog()
println(dog.speak()) // „Ham” — tip static Dog
}
Deși variabila animal indică o instanță Dog, se apelează funcția de extensie pentru Animal, deoarece tipul static al variabilei este Animal. Dacă speak() ar fi o metodă virtuală a clasei, s-ar apela Dog.speak(). Această proprietate este importantă de luat în considerare la proiectarea API-urilor bazate pe funcții de extensie.
Funcțiile de extensie nu sunt membri ai clasei — sunt funcții obișnuite care necesită import pentru a fi utilizate în alt fișier. Kotlin oferă două variante de import: după numele funcției sau cu redenumire.
// Import după nume
import com.example.extensions.isEmail
// Import cu alias (pentru a rezolva conflictele)
import com.example.extensions.isEmail as isValidEmail
// Apel după import
val valid = "test@test.com".isEmail()
Redenumirea prin as este utilă în caz de conflicte de nume — de exemplu, dacă două biblioteci oferă o funcție de extensie cu același nume pentru același tip. În acest caz, se poate importa una dintre ele cu un alt nume și se poate apela prin noul nume.
Funcțiile de extensie pot fi declarate la diferite niveluri:
| Nivel | Vizibilitate | Exemplu |
|---|---|---|
| Top-level | Întregul proiect după import | fun String.isEmail() |
| Member extension | În interiorul clasei proprietar | class A { fun B.ext() } |
| Local | În interiorul funcției | fun test() { fun String.ext() } |
Member extension functions — caz special când funcția de extensie este declarată în interiorul altei clase. În acest caz, în interiorul funcției de extensie sunt accesibili atât receptorul (this al funcției), cât și membrii clasei exterioare.
Dacă clasa are o metodă cu aceeași semnătură ca funcția de extensie, se apelează întotdeauna membrul clasei. Funcția de extensie nu suprascrie niciodată metoda clasei — este o decizie arhitecturală pentru a preveni suprascrierile accidentale.
class User {
fun greet() = "Salut din clasă"
}
fun User.greet() = "Hello from extension"
fun main() {
val user = User()
println(user.greet()) // „Salut din clasă”
}
Chiar dacă funcția de extensie este definită mai târziu și are aceeași semnătură, compilatorul va alege metoda clasei. Compilatorul Kotlin emite un avertisment la detectarea unei astfel de situații. Singura modalitate de a apela funcția de extensie în prezența metodei clasei este de a se adresa ei ca unei funcții obișnuite: greet(user).
În dezvoltarea Android, funcțiile de extensie au devenit un instrument standard pentru lucrul cu View, Context și fragmente. Biblioteca Android KTX este construită pe funcții de extensie, oferind învelitori convenabile pentru API-ul Android.
// Funcție de extensie pentru lucrul cu View
fun View.show() {
visibility = View.VISIBLE
}
fun View.hide() {
visibility = View.GONE
}
// Funcție de extensie pentru Context — toast
fun Context.toast(message: String) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
}
// Funcție de extensie ca constructor DSL
fun String.colorFormat(color: String): String =
"$color$this\u001b[0m"
Funcția de extensie toast() face apelurile dependente de Context concise: în loc să scrieți Toast.makeText(context, message, length).show() este suficient să scrieți context.toast("Text"). Aceasta reduce boilerplate-ul și face codul mai lizibil.
Întrebări frecvente
Nu, funcțiile de extensie folosesc dispecerizare statică. Dacă declarați o funcție de extensie pentru clasa de bază și una similară pentru subclasă, care dintre ele va fi apelată se determină de tipul static al variabilei în faza de compilare, nu de tipul real în runtime.
Se pot declara funcții de extensie pentru tipuri receptor nullable: fun String?.isNullOrEmail(). În interiorul unei astfel de funcții, this poate fi null, de aceea este necesar să folosiți operatori de apelare sigură sau verificare explicită de null.
Nu, funcțiile de extensie sunt compilate în metode statice. La nivel de bytecod, apelul unei funcții de extensie este identic cu apelul unei metode statice cu primul parametru receptor. Nici o reflecție sau dispecerizare dinamică.
Da, proprietățile de extensie funcționează similar cu funcțiile de extensie, dar nu pot stoca stare — doar getter și setter. De exemplu: val List<Int>.sum get() = reduce(Int::plus). Ele sunt de asemenea compilate în metode statice getter/setter.
În Java scrieți StringUtils.isEmail(str), iar în Kotlin — str.isEmail(). Diferența nu este doar de sintaxă: funcțiile de extensie suportă autocompletarea în IDE, îmbunătățesc lizibilitatea lanțurilor de apeluri și permit IDE-ului să sugereze funcții relevante pentru un anumit tip. În Java astfel de posibilități nu există.
Rezumat
Vom dezvolta o aplicație mobilă la cheie
IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.
Citiți și