extension property — механизъм в Kotlin, който позволява да добавяте нови свойства към съществуващи класове без наследяване и без промяна на изходния код. За разлика от extension functions, свойствата-разширения не могат да съхраняват състояние — те се декларират само с getter и опционално setter, тъй като нямат backing field. Според Kotlin Documentation, 2025, extension properties се компилират в статични методи getter и setter с receiver като първи параметър.
Основни моменти
extension property — е синтактична конструкция на Kotlin, която добавя свойство към съществуващ тип, без да променя неговата декларация. Свойството се декларира с посочване на receiver-типа и задължително съдържа getter. Ключовата разлика от обикновените свойства — липсата на backing field: extension property не може да съхранява данни, а ги изчислява на базата на receiver-обекта.
Според Kotlin Foundation Survey (2024), extension properties са по-малко популярни от extension functions — около 45% от разработчиците ги използват редовно. Това се дължи на ограничението за липса на състояние, което стеснява областта на приложение. Въпреки това, за изчислявани свойства, които са логически свързани с типа, extension properties са най-краткият вариант.
Extension properties се компилират в двойка статични методи getter и setter. На ниво на байткод няма разлика между извикването на extension property и extension function — и двете стават статични методи с параметър receiver. Според данни на JetBrains (Kotlin Docs, 2025), допълнителното натоварване е напълно липсващо.
Използвайте extension properties за къси изчислявани стойности, които трябва да изглеждат като свойства, а не като извиквания на методи — това подобрява четимостта на кода и спазва принципа за единен достъп.
За декларация на extension property се използва синтаксис, подобен на обикновено свойство, но с префикс на receiver-типа. 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. Това е не само по-кратко, но се чете като естествен език: „ако прозоръ0446ъ0442 е видим“. Въпреки простотата си, такива свойства значително подобряват четимостта на кода.
Често задавани въпроси
Не, 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(„gетIсVалид“) val String.isValid get() = true.
Не, extension properties не могат да бъдат декларирани с companion object като receiver. Това е ограничение на езика — extension property работи само с инстанции на типове, а companion object е статичен контекст. Използвайте extension functions на най-високо ниво или константи.
Минимално. Всяка extension property добавя един статичен getter метод (и опционален setter) към компилирания байткод. За сравнение: създаването на обвиващ клас с същото свойство добавя цял клас. Extension properties — по-лек подход за разширяване на функционалността.
Резюме
Ще разработим мобилно приложение под ключ
IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също