extension property — ένας μηχανισμός Kotlin που επιτρέπει την προσθήκη νέων ιδιοτήτων σε υφιστάμενες κλάσεις χωρίς κληρονομικότητα και χωρίς τροποποίηση του αρχικού κώδικα. Σε αντίθεση με τις extension functions, οι ιδιότητες-επεκτάσεις δεν μπορούν να αποθηκεύσουν κατάσταση — δηλώνονται μόνο με getter και προαιρετικά setter, επειδή δεν έχουν backing field. Σύμφωνα με Kotlin Documentation, 2025, τα extension properties μεταγλωττίζονται σε στατικές μεθόδους getter και setter με receiver ως πρώτη παράμετρο.
Βασικά σημεία
extension property — είναι μια συντακτική κατασκευή Kotlin που προσθέτει μια ιδιότητα σε έναν υφιστάμενο τύπο χωρίς να τροποποιεί τη δήλωσή του. Η ιδιότητα δηλώνεται με την προδιαγραφή του receiver-type και περιέχει υποχρεωτικά ένα getter. Η κλειδιή διαφορά από τις συνηθισμένες ιδιότητες — η απουσία backing field: η extension property δεν μπορεί να αποθηκεύσει δεδομένα, παρά μόνο να υπολογίζει βάσει του receiver-αντικειμένου.
Σύμφωνα με Kotlin Foundation Survey (2024), τα extension properties είναι λιγότερο δημοφιλή από τις extension functions — περίπου 45% των προγραματιστών τα χρησιμοποιεί τακτικά. Αυτό οφείλεται στον περιορισμό της έλλειψης κατάστασης, που στενεύει το πεδίο εφαρμογής. Παρόλα αυτά, για υπολογισμένες ιδιότητες που συνδέονται λογικά με τον τύπο, τα extension properties είναι η πιο σύντομη επιλογή.
Τα extension properties μεταγλωττίζονται σε ένα ζεύγος στατικών μεθόδων getter και setter. Σε επίπεδο bytecode, δεν υπάρχει διαφορά μεταξύ της κλήσης ενός extension property και μιας extension function — και τα δύο γίνονται στατικές μεθοδοι με παράμετρο receiver. Σύμφωνα με τα δεδομένα JetBrains (Kotlin Docs, 2025), η επιπλέον επιβάρυνση απουσιάζει πλήρως.
Χρησιμοποιείτε extension properties για σύντομες υπολογισμένες τιμές που πρέπει να φαίνονται ως ιδιότητες, όχι ως κλήσεις μεθόδων — αυτό βελτιώνει την αναγνωσιμότητα του κώδικα και ακολουθεί την αρχή της ομοιόμορφης πρόσβασης.
Για τη δήλωση μιας extension property χρησιμοποιείται σύνταξη παρόμοια με μια συνηθισμένη ιδιότητα, αλλά με πρόθεμα receiver-type. val δηλώνει μια read-only extension property με υποχρεωτικό getter, var — mutable με getter και προαιρετικό setter.
// Extension property μόνο για ανάγνωση
val String.isEmail: Boolean
get() = this.contains("@") && this.contains(".")
// Κλήση
val valid = "test@test.com".isEmail
Παρατηρείστε: η extension property καλείται χωρίς παρενθέσεις — str.isEmail, όχι str.isEmail(). Αυτή είναι η κλειδιή διαφορά μεταξύ extension property και extension function: η property μοιάζει με πεδίο, αν και στην πραγματικότητα υπολογίζεται μέσω getter.
Οι extension properties μπορούν να είναι γενικές — ο receiver μπορεί να χρησιμοποιεί generic παραμέτρους. Αυτό επιτρέπει τη δημιουργία καθολικών ιδιοτήτων που λειτουργούν με οποιονδήποτε τύπο συλλογής.
val List<T>.secondOrNull: T?
get() = if (size >= 2) this[1] else null
val items = listOf("a", "b", "c")
val second = items.secondOrNull // "b"
Η ιδιότητα secondOrNull λειτουργεί για οποιονδήποτε τύπο T, επιστρέφοντας το δεύτερο στοιχείο της λίστας ή null αν υπάρχουν λιγότερα από δύο στοιχεία. Αυτό είναι ένα τυπικό παράδειγμα όπου η extension property είναι πιο κατάλληλη από μια συνάρτηση — η πρόσβαση μοιάζει με ανάγνωση ενός πεδίου.
Η extension property δεν μπορεί να έχει backing field επειδή δεν προστίθεται στα μεταδεδομένα της κλάσης — υπάρχει μόνο ως ένα ζεύγος στατικών συναρτήσεων getter/setter. Backing field (η λέξη κλειδί field στην Kotlin) — είναι το εσωτερικό πεδίο της κλάσης που αποθηκεύει την τιμή της ιδιότητας. Η extension property δεν έχει πρόσβαση στην εσωτερική δομή της κλάσης.
// ❌ ΣΦΑΛΜΑ: η extension property δεν μπορεί να έχει backing field
var String.cachedValue: String
get() = "computed"
set(value) {
field = value // το πεδίο δεν είναι προσβάσιμο!
}
// ✅ ΣΩΣΤΑ: χρησιμοποιείστε εξωτερική αποθήκευση
val cache = MutableMap<String, String>()
var String.cachedValue: String
get() = cache[this] ?: ""
set(value) { cache[this] = value }
Το εξωτερικό Map στο παράδειγμα λύνει το πρόβλημα αποθήκευσης, αλλά δημιουργεί ένα άλλο — διαρροή μνήμης. Οι τιμές που λαμβάνονται μέσω extension property ζουν στο Map για πάντα αν δεν καθαριστούν. Αυτός ο περιορισμός καθιστά τα extension properties ακατάλληλα για προσωρινή αποθήκευση η αποθήκευση προσωρινών δεδομένων.
Για προσωρινή αποθήκευση, συνιστάται η χρήση WeakHashMap ή μηχανισμών με αυτόματο καθάρισμα. Η JetBrains συνιστά να αποφεύγετε τη χρήση var extension properties με εξωτερική αποθήκευση σε κώδικα παραγωγής χωρίς προσεκτική διαχείριση κύκλου ζωής.
Η επιλογή ανάμεσα σε extension property και extension function εξαρτάται από τη σημασιολογία: η ιδιότητα περιγράφει ένα χαρακτηριστικό του αντικειμένου, η συνάρτηση περιγράφει μια ενέργεια. Η αρχή της ομοιόμορφης πρόσβασης (Uniform Access Principle) λέει: ο πελάτης δεν πρέπει να γνωρίζει αν η τιμή υπολογίζεται ή αποθηκεύεται. Αν η τιμή μπορεί να παρουσιαστεί ως χαρακτηριστικό (μήκος, μέγεθος, κατάσταση) — χρησιμοποιείστε property.
| Κριτήριο | Extension property | Extension function |
|---|---|---|
| Κλήση | Χωρίς παρενθέσεις: obj.property | Με παρενθέσεις: obj.function() |
| Σημασιολογία | Χαρακτηριστικό, γνώρισμα | Ενέργεια, λειτουργία |
| Backing field | Δεν υποστηρίζεται | Δεν εφαρμόζεται |
| Παράμετροι | Μόνο getter/setter | Οποιεσδήποτε παράμετροι |
| Απόδοση | Ίδια (στατική μέθοδος) | Ίδια (στατική μέθοδος) |
| Παράδειγμα | text.length | text.isEmail() |
Ο κανόνας είναι απλός: αν η λειτουργία δέχεται παραμέτρους — χρησιμοποιείστε extension function. Αν είναι μια απλή υπολογισμένη τιμή χωρίς παραμέτρους — extension property. Σύμφωνα με Android Architecture Guide (Google, 2025), προτίμηση πρέπει να δίνεται στις extension properties για πρόσβαση σε δεδομένα και στις extension functions για λειτουργίες με παρενέργειες.
Η extension property με τη λέξη κλειδί var υποστηρίζει setter, αλλά χωρίς τη δυνατότητα αποθήκευσης της τιμής — ο setter συνήθως εκτελεί μια παρενέργεια ή αποθηκεύει δεδομένα σε εξωτερική αποθήκη. Η σύνταξη είναι ανάλογη με τις mutable ιδιότητες των κλάσεων.
// Mutable extension property με 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!
Η ιδιότητα lastChar — ένα κλασσικό παράδειγμα από την τεκμηρίωση Kotlin. Ο getter επιστρέφει τον τελευταίο χαρακτήρα του StringBuilder, ο setter τον αντικαθιστά με μια νέα τιμή. Παρατηρείστε: η κατάσταση αποθηκεύεται στον ίδιο τον StringBuilder (μέσω setCharAt), όχι σε ένα ξεχωριστό πεδίο — αυτή είναι σωστή χρήση της extension property.
Σε πραγματικά έργα, τα extension properties χρησιμοποιούνται συχνότερα για απλοποίηση της πρόσβασης σε δεδομένα συλλογών, υπολογισμό μεγεθών ή καταστάσεων στοιχείων UI και δημιουργία βολικής API πάνω σε υφιστάμενες κλάσεις. Η τυπική βιβλιοθήκη Kotlin χρησιμοποιεί ενεργά αυτόν τον μηχανισμό: size, indices, lastIndex για συλλογές — αυτά είναι extension properties.
// Extension properties για συλλογές
val List<Int>.sumFast: Int
get() = fold(0) { acc, i -> acc + i }
val String.half: String
get() = this.substring(0, length / 2)
// Extension property για Android View
val View.isVisible: Boolean
get() = visibility == View.VISIBLE
// Έλεγχος null μέσω ασφαλού receiver
val String?.isNullOrBlank: Boolean
get() = this == null || this.isBlank()
Η extension property isVisible για View — ένα παράδειγμα που κάθε προγραματιστής Android πρέπει να γνωρίζει. Αντί για view.visibility == View.VISIBLE μπορείτε να γράψετε view.isVisible. Δεν είναι μόνο πιο σύντομο, αλλά διαβάζεται σαν φυσική γλώσσα: “αν η προβολή είναι ορατή”. Παρά την απλότητα, τέτοιες ιδιότητες βελτιώνουν σημαντικά την αναγνωσιμότητα του κώδικα.
Συχνές ερωτήσεις
Όχι, τα extension properties δεν μπορούν να δηλωθούν για companion object ή object declaration. Ο μηχανισμός extension εφαρμόζεται μόνο σε κλάσεις, διεπαφές και nullable τύπους. Για object χρησιμοποιείστε συνηθισμένες συναρτήσεις κορυφαίου επιπέδου.
Η inline ιδιότητα (με τον τροποποιητή inline) — είναι ένας μηχανισμός Kotlin για την κλήση getter/setter χωρίς δημιουργία αντικειμένου-ιδιότητας. Extension property μεταγλώσσεται πάντα σε στατική μέθοδο, ενώ η inline ιδιότητα μεταγλώσσεται σε κλήση χωρίς περιτύλιγμα. Λύνουν διαφορετικά προβλήματα: η extension property προσθέτει ιδιότητα σε έναν υφιστάμενο τύπο, η inline βελτιστοποιεί τις κλήσεις των δικών της ιδιοτήτων.
Ναι, η extension property μπορεί να περιέχει σχόλια, αλλά μόνο σε επίπεδο δήλωσης. Δεν μπορείτε να σχολιάσετε ξεχωριστά το getter ή setter μιας extension property — σε αντίθεση με τις συνηθισμένες ιδιότητες κλάσης. Παράδειγμα: @JvmName(“getIsValid”) val String.isValid get() = true.
Όχι, τα extension properties δεν μπορούν να δηλωθούν με companion object ως receiver. Αυτός είναι ένας περιορισμός της γλώσσας — η extension property λειτουργεί μόνο με πρατότυπα τυπών, και το companion object είναι στατικό περιεχόμενο. Χρησιμοποιείστε extension functions κορυφαίου επιπέδου ή σταθερές.
Ελάχιστα. Κάθε extension property προσθέτει μία στατική μέθοδο getter (και προαιρετικά setter) στο μεταγλωττισμένο bytecode. Για σύγκριση: η δημιουργία μιας κλάσης-περιτύλιγματος με την ίδια ιδιότητα προσθέτει ολόκληρη την κλάση. Τα extension properties — ελαφρύτερη προσέγγιση για επέκταση λειτουργικότητας.
Περίληψη
Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση
Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.
Διαβάστε επίσης