extension property در Kotlin — چیست، نحوه نوشتن و کاربرد

نویسنده: IT Sectr منتشر شده: 2026-06-21 زمان مطالعه: 8 دقیقه

extension property — مکانیسمی در Kotlin است که به شما اجازه می‌دهد ویژگی‌های جدیدی به کلاس‌های موجود بدون ارث‌بری و بدون تغییر کد منبع اضافه کنید. به عکس extension functions، ویژگی‌های افزودنی نمی‌توانند وضعیت را ذخیره کنند — آنها فقط با getter و اختیاراً setter اعلام می‌شوند، زیرا backing field ندارند. به استناد به Kotlin Documentation, 2025، extension properties به روش‌های استاتیک getter و setter با receiver به عنوان پارامتر اول کامپایل می‌شوند.

نکات کلیدی

  • Extension property — ویژگی با receiver type، قابل دسترسی از طریق نحوه نوشتن ویژگی‌های Kotlin
  • Backing field ندارد — extension property نمی‌تواند وضعیت را ذخیره کند، فقط محاسبه می‌کند
  • Getter اجباری است، setter اختیاری — مانند extension functions معمولی اعلام می‌شوند
  • انواع nullable پشتیبانی می‌شوند: receiver می‌تواند با بررسی داخلی nullable باشد
  • Mutable extension property — فقط در صورت اعلام var با getter و setter

extension property در Kotlin چیست؟

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: val و var

برای اعلام extension property از نحوه نوشتنی مانند ویژگی معمولی استفاده می‌شود، اما با پیشوند نوع receiver. val یک read-only extension property با getter اجباری اعلام می‌کند، var — mutable با getter و setter اختیاری.

kotlin
// 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 محاسبه می‌شود.

Generic extension property

extension properties می‌توانند عمومی باشند — receiver می‌تواند از پارامترهای generic استفاده کند. این امکان ایجاد ویژگی‌های جهانی را فراهم می‌کند که با هر نوع کلکشن کار می‌کنند.

kotlin
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 نمی‌تواند وضعیت ذخیره کند

extension property نمی‌تواند backing field داشته باشد، زیرا به اطلاعات کلاس اضافه نمی‌شود — تنها به عنوان یک جفت تابع استاتیک getter/setter وجود دارد. Backing field (کلمه کلیدی field در Kotlin) — فیلد داخلی کلاس است که مقدار ویژگی را ذخیره می‌کند. extension property به ساختار داخلی کلاس دسترسی ندارد.

kotlin
// ❌ خطا: 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: کدام را انتخاب کنیم

انتخاب بین extension property و extension function به سمانتیک بستگی دارد: ویژگی یک ویژگی شیئٔ را توصیف می‌کند، در حالی که تابع یک عمل را. اصل دسترسی یکپارچه (Uniform Access Principle) می‌گوید: مشتری نباید بداند که مقدار محاسبه می‌شود یا ذخیره می‌شود. اگر مقدار را می‌توان به عنوان یک ویژگی (طول، اندازه، وضعیت) نمایش داد — از property استفاده کنید.

معیارExtension propertyExtension function
فراخوانیبدون پرانتز: obj.propertyبا پرانتز: obj.function()
سمانتیکویژگی، صفتعمل، عملیات
Backing fieldپشتیبانی نمی‌شودقابل اجرا نیست
پارامترهافقط getter/setterهر پارامتری
کارایییکسان (روش استاتیک)یکسان (روش استاتیک)
مثالtext.lengthtext.isEmail()

قانون ساده است: اگر عملیات پارامتر می‌گیرد — از extension function استفاده کنید. اگر یک مقدار محاسبه‌شده ساده بدون پارامتر است — extension property. به استناد Android Architecture Guide (Google, 2025)، برای دسترسی به داده‌ها از extension properties و برای عملیات با عوارض جانبی از extension functions استفاده کنید.

Mutable extension property با var و setter

extension property با کلمه کلیدی var از setter پشتیبانی می‌کند، اما بدون قابلیت ذخیره مقدار — setter معمولاً یک عمل جانبی انجام می‌دهد یا داده‌ها را در یک ذخیره خارجی ذخیره می‌کند. نحوه نوشتن مانند mutable ویژگی‌های کلاس‌ها است.

kotlin
// 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

در پروژه‌های واقعی، extension properties بیشتر برای ساده‌سازی دسترسی به داده‌های کلکشن‌ها، محاسبه اندازه یا وضعیت عناصر UI، و ایجاد API راحت بر روی کلاس‌های موجود استفاده می‌شوند. کتابخانه استاندارد Kotlin از این مکانیسم به‌طور فعال استفاده می‌کند: size، indices، lastIndex برای کلکشن‌ها — اینها extension properties هستند.

kotlin
// 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 property را برای companion object اعلام کرد؟

خیر، extension properties نمی‌توانند برای companion object یا object declaration اعلام شوند. مکانیسم extension فقط برای کلاس‌ها، اینترفیس‌ها و انواع nullable قابل اجراست. برای object از توابع سطح بالای معمولی استفاده کنید.

Extension property چه تفاوتی با ویژگی inline دارد؟

ویژگی inline (با مودیفکاتور inline) — مکانیسمی در Kotlin برای فراخوانی getter/setter بدون ایجاد شیئٔ ویژگی است. Extension property همیشه به یک روش استاتیک کامپایل می‌شود، در حالی که ویژگی inline به یک فراخوانی بدون پیچیدگی تبدیل می‌شود. آنها مسائل مختلفی را حل می‌کنند: extension property به نوع موجود ویژگی اضافه می‌کند، inline فراخوانی های ویژگی‌های خود را بهینه سازی می‌کند.

آیا extension property از انوتاسیون پشتیبانی می‌کند؟

بله، extension property می‌تواند انوتاسیون داشته باشد، اما فقط در سطح اعلام. نمی‌توان getter یا setter extension property را به طور جداگانه انوتاسیون کرد — بر خلاف ویژگی‌های معمولی کلاس. مثال: @JvmName(«getIsValid») val String.isValid get() = true.

آیا می‌توان از extension property با companion object کلاس استفاده کرد؟

خیر، extension properties را نمی‌توان با companion object به عنوان receiver اعلام کرد. این یک محدودیت زبان است — extension property فقط با نمونه‌هایی از انواع کار می‌کند، و companion object یک زمینه استاتیک است. از extension functions سطح بالا یا ثابت‌ها استفاده کنید.

آیا extension property بر اندازه APK تاثیر می‌گذارد؟

به صورت حداقل. هر extension property یک روش استاتیک getter (و اختیاراً setter) به کد بایت کامپایل‌شده اضافه می‌کند. برای مقایسه: ایجاد یک کلاس پیچیده با همین ویژگی یک کلاس کامل اضافه می‌کند. Extension properties — روشی سبک‌تر برای افزایش قابلیت‌هاست.

نتیجه‌گیری

  • Extension property — ویژگی محاسبه‌شده برای نوع موجود بدون ارث‌بری
  • Backing field ندارد — وضعیت ذخیره نمی‌شود، فقط از طریق getter محاسبه می‌شود
  • var با setter — ممکن است، اما برای نوشتن داده به ذخیره خارجی نیاز دارد
  • نحوه نوشتن — val/var با نوع receiver و getter اجباری
  • کارایی — overhead صفر، به روش استاتیک کامپایل می‌شود
  • کاربرد — ویژگی‌های محاسبه‌شده: طول، وضعیت، اندازه، بررسی‌ها
  • محدودیت — برای ذخیره وضعیت، ذخیره سازی بدون مدیریت حافظه مناسب نیست

ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد

IT Sectr از سال 2017 برنامه‌های iOS و Android را برای استارتاپ‌ها و کسب‌وکارها ایجاد می‌کند. ما به شما مشاوره می‌دهیم و بهترین راه‌حل را پیشنهاد خواهیم کرد.

بحث درباره پروژه

همچنین بخوانید