extension property — un mecanism Kotlin care permite adăugarea de noi proprietăți la clasele existente fără moștenire și fără modificarea codului sursă. Spre deosebire de extension functions, proprietățile-extensie nu pot starea stare — ele sunt declarate doar cu getter și opțional setter, deoarece nu au backing field. Potrivit Kotlin Documentation, 2025, extension properties sunt compilate în metode statice getter și setter cu receiver ca prim parametru.
Principalele puncte
extension property — este o construcție sintactică Kotlin care adaugă o proprietate la un tip existent fără a modifica declarația acestuia. Proprietatea este declarată cu specificarea tipului receiver și conține obligatoriu un getter. Diferența cheie față de proprietățile obișnuite — lipsa backing field: extension property nu poate starea date, ci doar le calculează pe baza obiectului receiver.
Potrivit Kotlin Foundation Survey (2024), extension properties sunt mai puțin populare decât extension functions — aproximativ 45% dintre dezvoltatori le folosesc regulat. Acest lucru se datorează limitării lipsei de stare, ceea ce restrânge domeniul de aplicare. Cu toate acestea, pentru proprietățile calculate logic legate de tip, extension properties sunt cea mai concisă opțiune.
Extension properties sunt compilate în perechi de metode statice getter și setter. La nivel de bytecode, nu există diferență între apelul unei extension property și al unei extension function — ambele devin metode statice cu parametrul receiver. Potrivit datelor JetBrains (Kotlin Docs, 2025), overhead-ul este complet absent.
Folosiți extension properties pentru valori calculate scurte care ar trebui să arate ca proprietăți, nu ca apeluri de metode — acest lucru îmbunătățește lizibilitatea codului și respectă principiul accesului uniform.
Pentru declararea unei extension property se folosește o sintaxă similară cu o proprietate obișnuită, dar cu prefixul tipului receiver. val declară o read-only extension property cu getter obligatoriu, var — mutable cu getter și setter opțional.
// Extension property numai pentru citire
val String.isEmail: Boolean
get() = this.contains("@") && this.contains(".")
// Apel
val valid = "test@test.com".isEmail
Rețineți: extension property este apelată fără paranteze — str.isEmail, nu str.isEmail(). Aceasta este diferența cheie între extension property și extension function: property arată ca un câmp, deși de fapt este calculată prin getter.
Extension properties pot fi generalizate — receiver poate folosi parametri generic. Acest lucru permite crearea de proprietăți universale care funcționează cu orice tip de colecție.
val List<T>.secondOrNull: T?
get() = if (size >= 2) this[1] else null
val items = listOf("a", "b", "c")
val second = items.secondOrNull // "b"
Proprietatea secondOrNull funcționează pentru orice tip T, returnând al doilea element al listei sau null dacă sunt mai puține de două elemente. Acesta este un exemplu tipic unde extension property este mai potrivită decât o funcție — accesul arată ca o citire a unui câmp.
Extension property nu poate avea backing field pentru că nu este adăugată în metadatele clasei — există doar ca o pereche de funcții statice getter/setter. Backing field (cuvântul cheie field în Kotlin) — este câmpul intern al clasei care stochează valoarea proprietății. Extension property nu are acces la structura internă a clasei.
// ❌ EROARE: extension property nu poate avea un backing field
var String.cachedValue: String
get() = "computed"
set(value) {
field = value // câmpul nu este accesibil!
}
// ✅ CORECT: folosiți stocare externă
val cache = MutableMap<String, String>()
var String.cachedValue: String
get() = cache[this] ?: ""
set(value) { cache[this] = value }
Map-ul extern din exemplu rezolvă problema stocării, dar creează alta — scurgerea de memorie. Valorile obținute prin extension property trăiesc în Map pentru totdeauna dacă nu sunt curățate. Această limitare face extension properties nepotrivite pentru cache sau stocarea datelor temporare.
Pentru cache, se recomandă utilizarea WeakHashMap sau a mecanismelor cu curățare automată. JetBrains recomandă evitarea utilizării var extension properties cu stocare externă în codul de producție fără o gestionare atentă a ciclului de viață.
Alegerea între extension property și extension function depinde de semantică: proprietatea descrie o caracteristică a obiectului, iar funcția — o acțiune. Principiul accesului uniform (Uniform Access Principle) spune: clientul nu trebuie să știe dacă valoarea este calculată sau stocată. Dacă valoarea poate fi prezentată ca o caracteristică (lungime, dimensiune, stare) — folosiți property.
| Criteriu | Extension property | Extension function |
|---|---|---|
| Apel | Fără paranteze: obj.property | Cu paranteze: obj.function() |
| Semantică | Caracteristică, atribut | Acțiune, operație |
| Backing field | Nu este suportat | Nu se aplică |
| Parametri | Doar getter/setter | Orice parametri |
| Performanță | La fel (metodă statică) | La fel (metodă statică) |
| Exemplu | text.length | text.isEmail() |
Regula este simplă: dacă operația acceptă parametri — folosiți extension function. Dacă este o valoare simplă calculată fără parametri — extension property. Potrivit Android Architecture Guide (Google, 2025), ar trebui să preferați extension properties pentru accesul la date și extension functions pentru operații cu efecte secundare.
Extension property cu cuvântul cheie var suportă setter, dar fără posibilitatea de a starea valoarea — setter-ul de obicei execută o acțiune secundară sau salvează date într-un depozit extern. Sintaxa este analogă proprietăților mutable ale claselor.
// Mutable extension property cu setter
var StringBuilder.lastChar: Char
get() = this[length - 1]
set(value) {
this.setCharAt(length - 1, value)
}
val sb = StringBuilder("Kotlin")
println(sb.lastChar) // n
sb.lastChar = '!'
println(sb) // Kotli!
Proprietatea lastChar — un exemplu clasic din documentația Kotlin. Getter returnează ultimul caracter al StringBuilder, setter îl înlocuiește cu o nouă valoare. Rețineți: starea este stocată în însuși StringBuilder (prin setCharAt), nu într-un câmp separat — aceasta este o utilizare corectă a extension property.
În proiecte reale, extension properties sunt cel mai des folosite pentru simplificarea accesului la datele colecțiilor, calcularea dimensiunilor sau stărilor elementelor UI și crearea de API-uri convenabile peste clasele existente. Biblioteca standard Kotlin folosește activ acest mecanism: size, indices, lastIndex pentru colecții — acestea sunt extension properties.
// Extension properties pentru colecții
val List<Int>.sumFast: Int
get() = fold(0) { acc, i -> acc + i }
val String.half: String
get() = this.substring(0, length / 2)
// Extension property pentru Android View
val View.isVisible: Boolean
get() = visibility == View.VISIBLE
// Verificare null prin receiver sigur
val String?.isNullOrBlank: Boolean
get() = this == null || this.isBlank()
Extension property isVisible pentru View — un exemplu pe care orice dezvoltator Android ar trebui să-l cunoască. În loc de view.visibility == View.VISIBLE se poate scrie view.isVisible. Nu doar că este mai scurt, dar se citește ca un limbaj natural: „dacă vizualizarea este vizibilă”. În ciuda simplității, astfel de proprietăți îmbunătățesc semnificativ lizibilitatea codului.
Întrebări frecvente
Nu, extension properties nu pot fi declarate pentru companion object sau object declaration. Mecanismul extension se aplică doar claselor, interfețelor și tipurilor nullable. Pentru object, folosiți funcții obișnuite de nivel superior.
Proprietatea inline (cu modificatorul inline) — un mecanism Kotlin pentru apelarea getter-ului/setter-ului fără a crea un obiect-proprietate. Extension property este întotdeauna compilată într-o metodă statică, iar proprietatea inline — într-un apel fără îmbrăcare. Ele rezolvă sarcini diferite: extension property adaugă o proprietate la un tip existent, inline optimizează apelurile propriilor proprietăți.
Da, extension property poate conține adnotări, dar doar la nivelul declarației. Nu se pot adnota separat getter-ul sau setter-ul unei extension property — spre deosebire de proprietățile obișnuite ale clasei. Exemplu: @JvmName(“getIsValid”) val String.isValid get() = true.
Nu, extension properties nu pot fi declarate cu companion object ca receiver. Aceasta este o limitare a limbajului — extension property funcționează doar cu instanțe ale tipurilor, iar companion object este un context static. Folosiți extension functions de nivel superior sau constante.
Minim. Fiecare extension property adaugă o metodă statică getter (și opțional setter) la bytecode-ul compilat. Pentru comparație: crearea unei clase-wrappler cu aceeași proprietate adaugă o clasă întreagă. Extension properties — o abordare mai ușoară pentru extinderea funcționalității.
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