Scope functions — چیست، انواع و کاربرد در Kotlin

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

Scope functions در Kotlin پنج تابع کتابخانه استاندارد (let, run, with, apply, also) هستند که یک بلوک کد را در زمینه یک شی اجرا می‌کنند. آنها در نحوه دسترسی به شی (از طریق it یا this) و مقدار بازگشتی (شی زمینه یا نتیجه لامبدا) متفاوت هستند. طبق مستندات Kotlin (2026)، انتخاب صحیح scope function کدهای تکراری را ۲۵-۴۰٪ کاهش می‌دهد. Scope functions به نوشتن مقداردهی اولیه، پیکربندی و تبدیل اشیا به سبک اعلانی کمک می‌کنند.

نکات اصلی

  • Scope functions — پنج تابع Kotlin (let, run, with, apply, also) برای کار در زمینه شی
  • زمینه از طریق this — run, with, apply از this استفاده می‌کنند و امکان فراخوانی مستقیم متدهای شی را می‌دهند
  • زمینه از طریق it — let و also شی را به عنوان پارامتر it با نام‌گذاری صریح منتقل می‌کنند
  • بازگشت شی — apply و also شی زمینه را برای زنجیره‌سازی فراخوانی‌ها برمی‌گردانند
  • بازگشت نتیجه — let, run و with آخرین عبارت لامبدا را برمی‌گردانند

Scope functions چیست؟

Scope functions (توابع محدوده) — مجموعه‌ای از پنج تابع (let, run, with, apply, also) هستند که در کتابخانه استاندارد Kotlin تعبیه شده‌اند. هر یک از آنها یک لامبدا می‌گیرد و به طور موقت محدوده دید را تغییر می‌دهد به طوری که داخل لامبدا کد در زمینه یک شی خاص اجرا می‌شود. این کار از تکرار نام شی در فراخوانی‌های متعدد جلوگیری می‌کند و عملیات مرتبط را در یک بلوک گروه‌بندی می‌کند.

دو ویژگی کلیدی scope functions را متمایز می‌کند: نحوه دسترسی به شی زمینه (از طریق this یا it) و مقدار بازگشتی (خود شی زمینه یا نتیجه لامبدا). دسترسی از طریق this شی را به گیرنده ضمنی تبدیل می‌کند — متدها و ویژگی‌ها بدون پیشوند فراخوانی می‌شوند. دسترسی از طریق it شی را به عنوان پارامتر صریح لامبدا منتقل می‌کند. بازگشت شی امکان ساخت زنجیره را می‌دهد، بازگشت نتیجه — انتساب مقدار تبدیل‌شده.

از نظر تاریخی، scope functions به عنوان جایگزینی برای الگوی کلاسیک Builder و متغیرهای موقت برای مقداردهی اولیه ظاهر شدند. به جای ایجاد یک کلاس builder جداگانه، برنامه‌نویس می‌نویسد apply { ویژگی1 = ...; ویژگی2 = ... }. این کار میزان کد را کاهش می‌دهد و مقداردهی اولیه را خواناتر می‌کند. در اکوسیستم Kotlin، scope functions در همه فریمورک‌ها و کتابخانه‌ها به عنوان یک اصطلاح استاندارد زبان استفاده می‌شوند.

مقایسه scope functions: this در مقابل it، بازگشت شی در مقابل نتیجه

انتخاب scope function مناسب توسط دو سوال تعیین می‌شود: آیا می‌خواهید از this یا it برای دسترسی به شی استفاده کنید، و آیا می‌خواهید خود شی یا نتیجه لامبدا را برگردانید. ترکیب دو ویژگی باینری چهار گزینه ممکن را می‌دهد، و with جایگاه ویژه‌ای به عنوان تنها تابعی که extension نیست دارد.

تابعدسترسیبازگشتExtension؟سناریوی معمول
letitنتیجه لامبدابلهتبدیل، بررسی null
runthisنتیجه لامبدابلهمحاسبه با زمینه
withthisنتیجه لامبداخیرگروه‌بندی فراخوانی‌ها
applythisشی زمینهبلهمقداردهی ویژگی‌ها
alsoitشی زمینهبلهاثرات جانبی، لاگ‌گیری

برای پیکربندی شی بدون بازگشت مقدار جدید از apply (دسترسی از طریق this) یا also (دسترسی از طریق it) استفاده کنید. برای محاسبه مقدار جدید بر اساس شی از let (دسترسی از طریق it) یا run (دسترسی از طریق this) استفاده کنید. برای گروه‌بندی فراخوانی‌ها بدون زنجیره از with استفاده کنید. پیروی از این طرح، کد را برای سایر توسعه‌دهندگان آشنا با Kotlin قابل پیش‌بینی می‌کند.

apply و also: پیکربندی با بازگشت شی

apply — scope function ای که شی زمینه را برمی‌گرداند و دسترسی به آن را از طریق this فراهم می‌کند. این ابزار ایده‌آلی برای مقداردهی اولیه ویژگی‌های شی پس از ایجاد است. در داخل بلوک apply می‌توان ویژگی‌ها را بدون تکرار نام شی تنظیم کرد. متد خود شی را برمی‌گرداند که امکان تعبیه apply در زنجیره‌ها یا استفاده در مقداردهی‌کننده‌ها را فراهم می‌کند.

kotlin
data class ServerConfig(
    var host: String = "localhost",
    var port: Int = 8080,
    var useTls: Boolean = false
)

val config = ServerConfig().apply {
    host = "api.example.com"
    port = 443
    useTls = true
}

در لیستینگ، apply ServerConfig را پیکربندی می‌کند و مستقیماً از طریق this به ویژگی‌ها دسترسی پیدا می‌کند (this حذف شده). بدون apply باید config.host = ..., config.port = ... نوشت — سه ارجاع تکراری به یک متغیر. بازگشت شی امکان ذخیره نتیجه پیکربندی را در val config فراهم می‌کند. also برخلاف apply، شی را از طریق it منتقل می‌کند و برای اثرات جانبی مناسب است.

kotlin
fun saveUser(user: User) {
    validate(user).also { result ->
        println("نتیجه اعتبارسنجی: $result")
    }
    val savedUser = user.also {
        log("Saving user ${it.id}")
        database.save(it)
    }
}

در مثال، also برای لاگ‌گیری استفاده می‌شود — با بازگرداندن شی اصلی، یک اثر جانبی (چاپ پیام) را بدون تغییر شی انجام می‌دهد. این یک زنجیره است: validate(user) User را برمی‌گرداند، also نتیجه را لاگ می‌کند و کاربر را جلو می‌فرستد. also همچنین برای افزودن عناصر به مجموعه‌ها یا اشکال‌زدایی در وسط زنجیره تبدیل مفید است.

let و run: تبدیل‌ها و محاسبات

let — scope function ای که نتیجه لامبدا را برمی‌گرداند و زمینه را از طریق it منتقل می‌کند. این تابع اصلی برای تبدیل‌های امن از نظر null است: اگر شی nullable باشد، ترکیب ?.let { } بلوک را فقط با مقدار غیر null اجرا می‌کند. let همچنین برای محدود کردن محدوده دید متغیرهای موقت و تبدیل‌های map-مانند از یک نوع به نوع دیگر استفاده می‌شود.

kotlin
fun findUser(id: Int): User?

val displayName = findUser(42)?.let { user ->
    "${user.name} (${user.email})"
} ?: "Unknown user"

// let با تبدیل مجموعه
val numbers = listOf("1", "2", "3")
val parsed = numbers.firstOrNull()?.let {
    it.toIntOrNull()
} ?: 0

در مثال اول، let از طریق ?. فراخوانی می‌شود — بلوک فقط در صورتی اجرا می‌شود که findUser غیر null برگرداند. لامبدا user را از طریق it می‌گیرد (برای وضوح می‌توان نام‌گذاری کرد) و یک رشته فرمت‌شده برمی‌گرداند. اگر کاربر پیدا نشد — عملگر Elvis ?: مقدار پیش‌فرض می‌دهد. در مثال دوم، let رشته را با null بالقوه به عدد تبدیل می‌کند.

run — مشابه let با دسترسی از طریق this، که نتیجه لامبدا را برمی‌گرداند. run برای محاسباتی که نیاز به زمینه شی دارند اما مقدار جدیدی برمی‌گردانند مناسب است. فراخوانی run() بدون شی (مانند run { ... }) یک محدوده دید موقت برای متغیرها ایجاد می‌کند. with — غیر extension با دسترسی از طریق this، مناسب برای گروه‌بندی فراخوانی‌ها بدون زنجیره.

kotlin
// run به عنوان محاسبه زمینه‌محور
val transformed = listOf(1, 2, 3).run {
    filter { it > 1 }.map { it * 10 }
}
println(transformed) // [20, 30]

// with برای گروه‌بندی فراخوانی‌ها
val info = with(StringBuilder()) {
    append("Name: ")
    append("Alice")
    toString()
}

در مثال، run روی لیست نتیجه فیلتر و map را برمی‌گرداند — خود تبدیل، نه لیست. with StringBuilder را به عنوان آرگومان می‌گیرد (نه از طریق نقطه) و داخل با this کار می‌کند: append, append, toString. نتیجه with — رشته info. انتخاب بین run و with اغلب سلیقه‌ای است، اما زمانی ترجیح داده می‌شود که شی از جای دیگری بدون زنجیره فراخوانی منتقل می‌شود.

with: زمینه بدون extension

with با سایر scope functions تفاوت دارد زیرا یک تابع extension نیست. این تابع شی را به عنوان آرگومان اول و لامبدا را به عنوان آرگومان دوم می‌گیرد. داخل لامبدا، شی از طریق this قابل دسترسی است. with برای گروه‌بندی فراخوانی‌های متدهای شی بدون ذخیره نتیجه یا زمانی که نیاز به «ورود» موقت به زمینه برای خواندن چند ویژگی است مناسب می‌باشد.

kotlin
val user = User("Alice", "alice@example.com", "Admin")

val summary = with(user) {
    "User $name has role $role and email $email"
}
println(summary)

// with برای مقداردهی اولیه کامپوننت UI
with(TextView(context)) {
    text = "Hello"
    textSize = 18.0f
    setTextColor(Color.BLUE)
}

در مثال اول، with رشته summary را تشکیل می‌دهد و مستقیماً از طریق this به ویژگی‌های user دسترسی پیدا می‌کند. بدون with باید «User ${user.name} has role ${user.role}» نوشت — تکرار مکرر user. در مثال دوم، with پیکربندی TextView را گروه‌بندی می‌کند: همه متدها و ویژگی‌ها روی یک شی بدون تکرار فراخوانی می‌شوند. مضرات with — عدم امکان استفاده در زنجیره، زیرا extension نیست و شی زمینه را برنمی‌گرداند.

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

برای مقداردهی اولیه شی کدام scope function را انتخاب کنیم؟

از apply استفاده کنید — شی را برمی‌گرداند و دسترسی از طریق this را فراهم می‌کند و امکان تنظیم ویژگی‌ها بدون تکرار نام شی را می‌دهد. also زمانی مناسب است که پارامتر صریح it برای تمایز از ویژگی‌های شی لازم باشد.

تفاوت let و run چیست؟

let زمینه را از طریق it (پارامتر صریح) منتقل می‌کند، run — از طریق this (گیرنده ضمنی). let برای بررسی‌های null و تبدیل‌ها مناسب است، run — برای محاسبات با زمینه. هر دو نتیجه لامبدا را برمی‌گردانند.

چه زمانی به جای apply از with استفاده کنیم؟

with extension نیست — شی را به عنوان آرگومان می‌گیرد. از with برای گروه‌بندی فراخوانی‌ها زمانی که زنجیره لازم نیست استفاده کنید. apply — extension ای است که شی را برمی‌گرداند، مناسب برای مقداردهی‌کننده‌ها و الگوهای builder.

آیا می‌توان یک scope function را درون دیگری تودرتو کرد؟

بله، اما مراقب this باشید — thisهای تودرتو باعث سردرگمی می‌شوند. برای زمینه‌های تودرتو از let یا also با نام‌های صریح استفاده کنید تا هنگام ارجاع به شی خارجی ابهام ایجاد نشود.

کدام scope function بیشترین کارایی را دارد؟

همه scope functions inline هستند، بنابراین سربار کلاس‌های ناشناس را ایجاد نمی‌کنند. تفاوت کارایی بین آنها ناچیز است. انتخاب باید بر اساس خوانایی و قراردادها باشد، نه سرعت.

خلاصه

  • Scope functions — پنج تابع inline Kotlin (let, run, with, apply, also) برای اجرای کد در زمینه شی
  • apply شی زمینه را با دسترسی از طریق this برمی‌گرداند — ایده‌آل برای مقداردهی ویژگی‌ها
  • also شی زمینه را با دسترسی از طریق it برمی‌گرداند — مناسب برای لاگ‌گیری و اثرات جانبی
  • let نتیجه لامبدا را با دسترسی از طریق it برمی‌گرداند — اساس تبدیل‌های امن از نظر null
  • run نتیجه لامبدا را با دسترسی از طریق this برمی‌گرداند — مناسب برای محاسبات با زمینه
  • with extension نیست و نتیجه لامبدا را برمی‌گرداند — مناسب برای گروه‌بندی فراخوانی‌ها بدون زنجیره
  • ترکیب ویژگی‌ها (this/it و بازگشت شی/نتیجه) انتخاب scope function را برای هر کار تعیین می‌کند

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

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

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

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