تابع توسعه در Kotlin: چیست، نحو و کاربرد

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

تابع توسعه — مکانیزم Kotlin که امکان افزودن متدهای جدید به کلاس‌های موجود بدون وراثت و بدون تغییر کد منبع را فراهم می‌کند. تابع با پیشوند نوع گیرنده اعلان می‌شود و به عنوان متد معمولی این نوع فراخوانی می‌شود. طبق Kotlin Documentation, 2025، توابع توسعه به متدهای استاتیک با اولین پارامتر گیرنده کامپایل می‌شوند که سربار صفر را در زمان اجرا نسبت به فراخوانی‌های معمولی تضمین می‌کند.

نکات اصلی

  • تابع توسعه — متدی با نوع گیرنده که به عنوان عضو کلاس بدون وراثت فراخوانی می‌شود
  • نوع گیرنده قبل از نام تابع با نقطه مشخص می‌شود: fun Type.method()
  • توزیع ایستا — تابع توسعه مجازی نیست، بر اساس نوع متغیر فراخوانی می‌شود
  • Import اجباری است: توابع توسعه عضو کلاس نیستند و نیاز به import دارند
  • اعضای کلاس اولویت دارند: اگر متد مطابقت داشته باشد، عضو کلاس فراخوانی می‌شود نه تابع توسعه

تابع توسعه در Kotlin چیست؟

تابع توسعه تابعی است که خارج از کلاس اعلان می‌شود اما به عنوان متد آن فراخوانی می‌شود. این تابع نوع گیرنده‌ای را می‌پذیرد که متد جدید به آن افزوده می‌شود. در داخل تابع، کلمه کلیدی this به نمونه نوع گیرنده اشاره می‌کند. این رویکرد امکان توسعه کلاس‌های final، کلاس‌های کتابخانه‌های شخص ثالث و حتی انواع اولیه را بدون ایجاد لفاف فراهم می‌کند.

برخلاف Java که برای افزودن متد به یک کلاس موجود یا به وراثت نیاز دارد یا به کلاس ابزاری استاتیک، Kotlin راه‌حلی ظریف بدون boilerplate ارائه می‌دهد. بر اساس داده‌های تحقیق Kotlin Foundation (2024)، توابع توسعه در میان پنج ویژگی پرکاربرد زبان قرار دارند — 78% توسعه‌دهندگان Kotlin در کار روزانه خود از آنها استفاده می‌کنند.

کامپایلر Kotlin تابع توسعه را به یک متد استاتیک تبدیل می‌کند که در آن نمونه گیرنده به عنوان اولین آرگومان ارسال می‌شود. این بدان معناست که توابع توسعه بایت کد کلاس هدف را تغییر نمی‌دهند و کپسوله‌سازی را نقض نمی‌کنند — فقط فیلدها و متدهای عمومی گیرنده قابل دسترسی هستند.

از توابع توسعه برای نوشتن توابع ابزاری تمیز استفاده کنید که به طور منطقی به یک نوع خاص وابسته هستند اما نمی‌توانند به کد منبع آن اضافه شوند.

نحو تابع توسعه: اعلان و فراخوانی

نحو پایه تابع توسعه: نام نوع گیرنده، نقطه، نام تابع، سپس پارامترها و نوع بازگشتی. در داخل تابع، گیرنده از طریق this در دسترس است.

kotlin
// تابع توسعه برای String
fun String.isEmail(): Boolean {
    return this.contains("@") && this.contains(".")
}

// به عنوان متد معمولی String فراخوانی می‌شود
val result = "user@example.com".isEmail() // true

در این مثال isEmail() برای تمام رشته‌ها در دسترس می‌شود. در داخل تابع، this خود رشته‌ای است که متد روی آن فراخوانی می‌شود. Kotlin در بیشتر موارد اجازه حذف this را می‌دهد — می‌توان به جای this.contains("@") فقط contains("@") نوشت.

تابع توسعه با پارامترها

توابع توسعه می‌توانند پارامترهای اضافی دریافت کنند و مقادیر هر نوعی را برگردانند. این آنها را به توابع کاملی تبدیل می‌کند، نه فقط شکر نحوی.

kotlin
fun List<Int>.defaultIfEmpty(default: Int): List<Int> {
    if (this.isEmpty()) return listOf(default)
    return this
}

val populated = listOf(1, 2).defaultIfEmpty(0)
val empty = listOf<Int>().defaultIfEmpty(0)

پارامتر default مقدار بازگشتی برای لیست خالی را تعیین می‌کند. تابع توسعه می‌تواند generic باشد — نوع List عمومی است، اگرچه در مثال از List خاص استفاده شده است.

توزیع ایستا و گیرنده

توابع توسعه در Kotlin از توزیع ایستا استفاده می‌کنند، نه مجازی. این تفاوت کلیدی با متدهای معمولی کلاس‌هاست. اینکه کدام تابع توسعه فراخوانی می‌شود در مرحله کامپایل بر اساس نوع ایستای متغیر تعیین می‌شود، نه نوع واقعی آن در زمان اجرا.

kotlin
open class Animal
class Dog : Animal()

fun Animal.speak() = "صدای حیوان"
fun Dog.speak() = "واق"

fun test() {
    val animal: Animal = Dog()
    println(animal.speak()) // «صدای حیوان» — نوع ایستا Animal

    val dog: Dog = Dog()
    println(dog.speak()) // «واق» — نوع ایستا Dog
}

با وجود اینکه متغیر animal به نمونه Dog اشاره می‌کند، تابع توسعه برای Animal فراخوانی می‌شود، زیرا نوع ایستای متغیر Animal است. اگر speak() یک متد مجازی کلاس بود، Dog.speak() فراخوانی می‌شد. این ویژگی هنگام طراحی API مبتنی بر توابع توسعه مهم است.

import و محدوده دید توابع توسعه

توابع توسعه عضو کلاس نیستند — آنها توابع معمولی هستند که برای استفاده در فایل دیگر نیاز به import دارند. Kotlin دو گزینه import ارائه می‌دهد: با نام تابع یا با تغییر نام.

kotlin
// import با نام
import com.example.extensions.isEmail

// import با نام مستعار (برای حل تداخل‌ها)
import com.example.extensions.isEmail as isValidEmail

// فراخوانی پس از import
val valid = "test@test.com".isEmail()

تغییر نام با as در تداخلات نام مفید است — مثلاً اگر دو کتابخانه تابع توسعه با نام یکسان برای یک نوع ارائه دهند. در این صورت می‌توان یکی را با نام دیگر import کرد و از طریق نام جدید فراخوانی نمود.

محدوده دید

توابع توسعه را می‌توان در سطوح مختلف اعلان کرد:

سطحدیدمثال
Top-levelکل پروژه پس از importfun String.isEmail()
Member extensionداخل کلاس مالکclass A { fun B.ext() }
Localداخل تابعfun test() { fun String.ext() }

Member extension functions — حالت خاصی که تابع توسعه داخل کلاس دیگری اعلان می‌شود. در این حالت داخل تابع توسعه هم گیرنده (this تابع) و هم اعضای کلاس خارجی قابل دسترسی هستند.

اولویت اعضای کلاس بر توابع توسعه

اگر کلاس متدی با همان امضای تابع توسعه داشته باشد، همیشه عضو کلاس فراخوانی می‌شود. تابع توسعه هرگز متد کلاس را بازنویسی نمی‌کند — این یک تصمیم معماری برای جلوگیری از بازنویسی‌های تصادفی است.

kotlin
class User {
    fun greet() = "سلام از کلاس"
}

fun User.greet() = "Hello from extension"

fun main() {
    val user = User()
    println(user.greet()) // «سلام از کلاس»
}

حتی اگر تابع توسعه بعداً تعریف شود و همان امضا را داشته باشد، کامپایلر متد کلاس را انتخاب می‌کند. کامپایلر Kotlin هنگام تشخیص چنین وضعیتی اخطار می‌دهد. تنها راه فراخوانی تابع توسعه در حضور متد کلاس، مراجعه به آن به عنوان یک تابع معمولی است: greet(user).

مثال‌های عملی توابع توسعه

در توسعه Android، توابع توسعه به ابزاری استاندارد برای کار با View، Context و fragmentها تبدیل شده‌اند. کتابخانه Android KTX بر روی توابع توسعه ساخته شده است و لفاف‌های مناسبی برای API اندروید ارائه می‌دهد.

kotlin
// تابع توسعه برای کار با View
fun View.show() {
    visibility = View.VISIBLE
}

fun View.hide() {
    visibility = View.GONE
}

// تابع توسعه برای Context — toast
fun Context.toast(message: String) {
    Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
}

// تابع توسعه به عنوان سازنده DSL
fun String.colorFormat(color: String): String =
    "$color$this\u001b[0m"

تابع توسعه toast() فراخوانی‌های وابسته به Context را مختصر می‌کند: به جای نوشتن Toast.makeText(context, message, length).show() کافی است context.toast("متن") بنویسید. این کار boilerplate را کاهش می‌دهد و کد را خواناتر می‌کند.

سوالات متداول

آیا می‌توان تابع توسعه را در زیرکلاس بازنویسی کرد؟

خیر، توابع توسعه از توزیع ایستا استفاده می‌کنند. اگر تابع توسعه برای کلاس پایه و همین طور برای زیرکلاس اعلان شود، اینکه کدام فراخوانی شود بر اساس نوع ایستای متغیر در مرحله کامپایل تعیین می‌شود، نه نوع واقعی در زمان اجرا.

توابع توسعه با انواع nullable چگونه کار می‌کنند؟

می‌توان توابع توسعه را برای انواع گیرنده nullable اعلان کرد: fun String?.isNullOrEmail(). در داخل چنین تابعی this می‌تواند null باشد، بنابراین باید از عملگرهای فراخوانی امن یا بررسی صریح null استفاده کرد.

آیا توابع توسعه سربار عملکردی دارند؟

خیر، توابع توسعه به متدهای استاتیک کامپایل می‌شوند. در سطح بایت کد، فراخوانی تابع توسعه با فراخوانی متد استاتیک با اولین پارامتر گیرنده یکسان است. هیچ انعکاس یا توزیع پویایی وجود ندارد.

آیا می‌توان ویژگی توسعه برای کلاس اعلان کرد؟

بله، ویژگی‌های توسعه مشابه توابع توسعه کار می‌کنند اما نمی‌توانند حالت ذخیره کنند — فقط getter و setter. مثلاً: val List<Int>.sum get() = reduce(Int::plus). آنها نیز به متدهای استاتیک getter/setter کامپایل می‌شوند.

تفاوت توابع توسعه با متدهای کلاس‌های ابزاری Java چیست؟

در Java می‌نویسید StringUtils.isEmail(str)، در Kotlin — str.isEmail(). تفاوت فقط در نحو نیست: توابع توسعه از تکمیل خودکار در IDE پشتیبانی می‌کنند، خوانایی زنجیره‌های فراخوانی را بهبود می‌بخشند و به IDE امکان می‌دهند توابع مرتبط را برای نوع خاص پیشنهاد دهد. در Java چنین قابلیت‌هایی وجود ندارد.

خلاصه

  • تابع توسعه — متدی با نوع گیرنده که به عنوان عضو کلاس بدون وراثت فراخوانی می‌شود
  • توزیع ایستا — انتخاب تابع بر اساس نوع ایستا، نه نوع زمان اجرا
  • سربار صفر — به متد استاتیک با گیرنده به عنوان اولین پارامتر کامپایل می‌شود
  • import برای استفاده در فایل‌های دیگر اجباری است، alias از طریق as ممکن است
  • اعضای کلاس بر توابع توسعه با امضای یکسان اولویت دارند
  • Member extensions — توسعه‌های اعلان شده داخل کلاس، به اعضای آن دسترسی دارند
  • کاربرد عملی — ابزارهایی برای String، View، Context، مجموعه‌ها و ساخت DSL

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

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

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

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