extension property — механізм Kotlin, що дозволяє додавати нові властивості до існуючих класів без успадкування та без зміни вихідного коду. На відміну від extension functions, extension properties не можуть зберігати стан — вони оголошуються лише з геттером і, опціонально, сеттером, оскільки не мають backing field. Згідно з Kotlin Documentation, 2025, extension properties компілюються в статичні методи getter і setter з receiver як першим параметром.
Головне
Extension property — це синтаксична конструкція Kotlin, яка додає властивість до існуючого типу без зміни його оголошення. Властивість оголошується з receiver type і обов'язково містить геттер. Ключова відмінність від звичайних властивостей — відсутність backing field: extension property не може зберігати дані, а тільки обчислює їх на основі receiver-об'єкта.
Згідно з опитуванням Kotlin Foundation (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 type. val оголошує read-only extension property з обов'язковим геттером, var — mutable з геттером і опціональним сеттером.
// 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 виглядає як поле, хоча насправді обчислюється через геттер.
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 property непридатними для кешування або зберігання тимчасових даних.
Для кешування рекомендується використовувати WeakHashMap або механізми з автоматичним очищенням. JetBrains рекомендує уникати використання var extension properties із зовнішнім сховищем у production-коді без ретельного управління життєвим циклом.
Вибір між 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 підтримує сеттер, але без можливості зберігати значення — сеттер зазвичай виконує побічну дію або зберігає дані у зовнішньому сховищі. Синтаксис аналогічний mutable властивостям класів.
// Змінна extension property з сеттером
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. Геттер повертає останній символ StringBuilder, сеттер замінює його на нове значення. Зверніть увагу: стан зберігається в самому 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. Це не тільки коротше, але й читається як природна мова: «якщо view видимий». Незважаючи на простоту, такі властивості суттєво покращують читабельність коду.
Поширені запитання
Ні, extension properties не можуть бути оголошені для companion object або object declaration. Механізм extension застосовний тільки до класів, інтерфейсів і nullable-типів. Для object використовуйте звичайні функції верхнього рівня.
Inline-властивість (з модифікатором inline) — механізм Kotlin для виклику геттера/сеттера без створення об'єкта-властивості. 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 функції верхнього рівня або константи.
Мінімально. Кожне extension property додає один статичний метод getter (і опціонально setter) у скомпільований байт-код. Для порівняння: створення класу-обгортки з такою ж властивістю додає цілий клас. Extension properties — більш легкий підхід до розширення функціональності.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також