extension property — un meccanismo Kotlin che permette di aggiungere nuove proprietà a classi esistenti senza ereditarietà e senza modificare il codice sorgente. A differenza delle extension functions, le extension properties non possono memorizzare stato — vengono dichiarate solo con un getter e, opzionalmente, un setter, poiché non hanno un backing field. Secondo Kotlin Documentation, 2025, le extension properties vengono compilate in metodi statici getter e setter con il receiver come primo parametro.
Punti chiave
Una extension property è una costruzione sintattica in Kotlin che aggiunge una proprietà a un tipo esistente senza modificarne la dichiarazione. La proprietà viene dichiarata con un receiver type e deve contenere un getter. La differenza chiave rispetto alle proprietà normali è l'assenza di backing field: una extension property non può memorizzare dati, solo calcolarli basandosi sull'oggetto receiver.
Secondo il Sondaggio Kotlin Foundation (2024), le extension properties sono meno popolari delle extension functions — circa il 45% degli sviluppatori le usa regolarmente. Ciò è dovuto alla limitazione di non avere stato, che restringe l'ambito di applicazione. Tuttavia, per proprietà calcolate logicamente legate a un tipo, le extension properties sono l'opzione più concisa.
Le extension properties vengono compilate in una coppia di metodi statici getter e setter. A livello di bytecode, non c'è differenza tra chiamare una extension property e chiamare una extension function — entrambe diventano metodi statici con un parametro receiver. Secondo JetBrains (Kotlin Docs, 2025), non c'è alcun overhead.
Usa le extension properties per valori calcolati brevi che devono sembrare proprietà piuttosto che chiamate a metodi — questo migliora la leggibilità del codice e segue il Principio di Accesso Uniforme.
Per dichiarare una extension property, usa una sintassi simile a una proprietà normale, ma con il prefisso del receiver type. val dichiara una extension property di sola lettura con un getter obbligatorio, var ne dichiara una mutabile con un getter e un setter opzionale.
// Extension property di sola lettura
val String.isEmail: Boolean
get() = this.contains("@") && this.contains(".")
// Chiamata
val valid = "test@test.com".isEmail
Nota: una extension property viene chiamata senza parentesi — str.isEmail, non str.isEmail(). Questa è la differenza chiave tra una extension property e una extension function: una proprietà sembra un campo, anche se in realtà viene calcolata tramite un getter.
Le extension properties possono essere generiche — il receiver può usare parametri di tipo generico. Questo permette di creare proprietà universali che funzionano con qualsiasi tipo di collezione.
val List<T>.secondOrNull: T?
get() = if (size >= 2) this[1] else null
val items = listOf("a", "b", "c")
val second = items.secondOrNull // "b"
La proprietà secondOrNull funziona per qualsiasi tipo T, restituendo il secondo elemento della lista o null se ci sono meno di due elementi. Questo è un esempio tipico in cui una extension property è più appropriata di una funzione — il suo accesso sembra la lettura di un campo.
Una extension property non può avere un backing field perché non viene aggiunta ai metadati della classe — esiste solo come coppia di funzioni statiche getter/setter. Backing field (la parola chiave field in Kotlin) è un campo interno della classe che memorizza il valore della proprietà. Una extension property non ha accesso alla struttura interna della classe.
// ❌ ERRORE: extension property non può avere un backing field
var String.cachedValue: String
get() = "computed"
set(value) {
field = value // campo non accessibile!
}
// ✅ CORRETTO: usa archiviazione esterna
val cache = MutableMap<String, String>()
var String.cachedValue: String
get() = cache[this] ?: ""
set(value) { cache[this] = value }
Una Map esterna nell'esempio risolve il problema di memorizzazione ma ne crea un altro — una perdita di memoria. I valori ottenuti tramite una extension property vivono nella Map per sempre se non vengono puliti. Questa limitazione rende le extension properties inadatte per la memorizzazione nella cache o per dati temporanei.
Per la cache, si raccomanda di usare un WeakHashMap o meccanismi con pulizia automatica. JetBrains raccomanda di evitare l'uso di var extension properties con memorizzazione esterna in codice di produzione senza un'attenta gestione del ciclo di vita.
La scelta tra una extension property e una extension function dipende dalla semantica: una proprietà descrive una caratteristica di un oggetto, mentre una funzione descrive un'azione. Il Principio di Accesso Uniforme afferma: il cliente non deve sapere se un valore viene calcolato o memorizzato. Se il valore può essere rappresentato come una caratteristica (lunghezza, dimensione, stato) — usa una proprietà.
| Criterio | Extension property | Extension function |
|---|---|---|
| Chiamata | Senza parentesi: obj.property | Con parentesi: obj.function() |
| Semantica | Caratteristica, attributo | Azione, operazione |
| Backing field | Non supportato | Non applicabile |
| Parametri | Solo getter/setter | Qualsiasi parametro |
| Prestazioni | Uguali (metodo statico) | Uguali (metodo statico) |
| Esempio | text.length | text.isEmail() |
La regola è semplice: se l'operazione accetta parametri — usa una extension function. Se è un semplice valore calcolato senza parametri — usa una extension property. Secondo la Guida all'Architettura Android (Google, 2025), si dovrebbe dare preferenza alle extension properties per l'accesso ai dati e alle extension functions per le operazioni con effetti collaterali.
Una extension property con la parola chiave var supporta un setter, ma senza la capacità di memorizzare un valore — il setter di solito esegue un effetto collaterale o salva dati in un archivio esterno. La sintassi è simile alle proprietà mutabili di classe.
// Extension property mutabile con 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!
La proprietà lastChar è un esempio classico dalla documentazione Kotlin. Il getter restituisce l'ultimo carattere di StringBuilder, il setter lo sostituisce con un nuovo valore. Nota: lo stato è memorizzato nello stesso StringBuilder (tramite setCharAt), non in un campo separato — questo è un uso corretto di una extension property.
Nei progetti reali, le extension properties sono più spesso usate per semplificare l'accesso ai dati delle collezioni, calcolare dimensioni o stati di elementi UI e creare un'API comoda sopra classi esistenti. La libreria standard Kotlin usa attivamente questo meccanismo: size, indices, lastIndex per le collezioni sono extension properties.
// Extension properties per collezioni
val List<Int>.sumFast: Int
get() = fold(0) { acc, i -> acc + i }
val String.half: String
get() = this.substring(0, length / 2)
// Extension property per Android View
val View.isVisible: Boolean
get() = visibility == View.VISIBLE
// Controllo null tramite receiver sicuro
val String?.isNullOrBlank: Boolean
get() = this == null || this.isBlank()
L'extension property isVisible per View è un esempio che ogni sviluppatore Android dovrebbe conoscere. Invece di view.visibility == View.VISIBLE, puoi scrivere view.isVisible. Questo non è solo più corto, ma si legge anche come linguaggio naturale: "se la view è visibile". Nonostante la loro semplicità, tali proprietà migliorano significativamente la leggibilità del codice.
Domande frequenti
No, le extension properties non possono essere dichiarate per un companion object o una object declaration. Il meccanismo di estensione si applica solo a classi, interfacce e tipi nullable. Per object, usa funzioni normali di livello superiore.
Una inline property (con il modificatore inline) è un meccanismo Kotlin per chiamare un getter/setter senza creare un oggetto proprietà. Una extension property viene sempre compilata in un metodo statico, mentre una inline property viene compilata in una chiamata senza wrapper. Risolvono problemi diversi: una extension property aggiunge una proprietà a un tipo esistente, mentre inline ottimizza le chiamate alle proprie proprietà.
Sì, una extension property può avere annotazioni, ma solo a livello di dichiarazione. Non è possibile annotare separatamente il getter o il setter di una extension property — a differenza delle proprietà di classe normali. Esempio: @JvmName("getIsValid") val String.isValid get() = true.
No, le extension properties non possono essere dichiarate con un companion object come receiver. Questa è una limitazione del linguaggio — una extension property funziona solo con istanze di tipo, mentre un companion object è un contesto statico. Usa funzioni di estensione di livello superiore o costanti.
Minimamente. Ogni extension property aggiunge un metodo getter statico (e opzionalmente un setter) al bytecode compilato. Per confronto, creare una classe wrapper con la stessa proprietà aggiunge un'intera classe. Le extension properties sono un approccio più leggero per estendere le funzionalità.
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche