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)، هیچ سرریال overhead وجود ندارد.
از 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 نوشت. این نه تنها کوتاهتر است، بلکه مانند زبان طبیعی خوانده میشود: «اگر نمایش دیده شود». با وجود سادگی، چنین ویژگیهایی خواندنی کد را به طور قابل توجهی بهبود میبخشند.
سوالات متداول
خیر، 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) به کد بایت کامپایلشده اضافه میکند. برای مقایسه: ایجاد یک کلاس پیچیده با همین ویژگی یک کلاس کامل اضافه میکند. Extension properties — روشی سبکتر برای افزایش قابلیتهاست.
نتیجهگیری
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید