دوال النطاق في Kotlin هي خمس دوال من المكتبة القياسية (let, run, with, apply, also) تنفذ كتلة من التعليمات البرمجية في سياق كائن. تختلف في طريقة الوصول إلى الكائن (من خلال it أو this) والقيمة المعادة (كائن السياق أو نتيجة lambda). وفقًا لوثائق Kotlin (2026)، فإن الاختيار الصحيح لدالة النطاق يقلل الكود التكراري بنسبة 25–40%. دوال النطاق تساعد في كتابة تهيئة الكائنات وتكوينها وتحويلاتها بأسلوب تعريفي.
النقاط الرئيسية
دوال النطاق هي مجموعة من خمس دوال (let, run, with, apply, also) مدمجة في المكتبة القياسية لـ Kotlin. كل منها تأخذ lambda وتغير النطاق مؤقتًا بحيث يتم تنفيذ الكود داخل lambda في سياق كائن معين. هذا يلغي تكرار اسم الكائن عند الاستدعاءات المتعددة ويجمع العمليات المرتبطة في كتلة واحدة.
خاصيتان رئيسيتان تميزان دوال النطاق: طريقة الوصول إلى كائن السياق (من خلال this أو من خلال it) والقيمة المعادة (كائن السياق نفسه أو نتيجة lambda). الوصول من خلال this يجعل الكائن مستقبلًا ضمنيًا — يتم استدعاء الدوال والخصائص بدون بادئة. الوصول من خلال it يمرر الكائن كمعامل صريح لـ lambda. إرجاع الكائن يسمح ببناء السلاسل، وإرجاع النتيجة يسمح بتعيين القيمة المحولة.
تاريخيًا، ظهرت دوال النطاق كبديل للنمط الكلاسيكي Builder والمتغيرات المؤقتة للتهيئة. بدلاً من إنشاء فئة builder منفصلة، يكتب المطور apply { خاصية1 = ...; خاصية2 = ... }. هذا يقلل حجم الكود ويجعل التهيئة أكثر قابلية للقراءة. في نظام Kotlin البيئي، تُستخدم دوال النطاق في جميع الأطر والمكتبات كأسلوب لغوي قياسي.
يتم تحديد اختيار دالة النطاق الصحيحة من خلال سؤالين: هل تريد استخدام this أو it للوصول إلى الكائن، وهل تريد إرجاع الكائن نفسه أم نتيجة lambda. مزيج الخاصيتين الثنائيتين يعطي أربعة خيارات ممكنة، و with تحتل مكانة خاصة كالدالة الوحيدة التي ليست امتدادًا.
| الدالة | الوصول | الإرجاع | امتداد؟ | السيناريو النموذجي |
|---|---|---|---|---|
| let | it | نتيجة lambda | نعم | تحويل، تحقق من null |
| run | this | نتيجة lambda | نعم | حساب مع سياق |
| with | this | نتيجة lambda | لا | تجميع الاستدعاءات |
| apply | this | كائن السياق | نعم | تهيئة الخصائص |
| also | it | كائن السياق | نعم | تأثيرات جانبية، تسجيل |
لتكوين كائن دون إرجاع قيمة جديدة، استخدم apply (الوصول من خلال this) أو also (الوصول من خلال it). لحساب قيمة جديدة بناءً على كائن، استخدم let (الوصول من خلال it) أو run (الوصول من خلال this). لتجميع الاستدعاءات دون سلسلة، استخدم with. اتباع هذا المخطط يجعل الكود قابلاً للتنبؤ للمطورين الآخرين الملمين بـ Kotlin.
apply هي دالة نطاق تعيد كائن السياق وتوفر الوصول إليه من خلال this. إنها الأداة المثالية لتهيئة خصائص الكائن بعد إنشائه. داخل كتلة apply يمكن تعيين الخصائص دون تكرار اسم الكائن. تعيد الدالة الكائن نفسه، مما يسمح بإدراج apply في سلاسل أو استخدامه في المهيآت.
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 وهي مناسبة للإجراءات الجانبية.
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 هي دالة نطاق تعيد نتيجة lambda وتمرر السياق من خلال it. إنها الدالة الرئيسية للتحويلات الآمنة مع null: إذا كان الكائن nullable، فإن التركيبة ?.let { } تنفذ الكتلة فقط للقيم غير الفارغة. تُستخدم let أيضًا لتحديد نطاق المتغيرات المؤقتة وللتحويلات الشبيهة بـ map من نوع إلى آخر.
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 قيمة غير فارغة. تأخذ lambda المستخدم من خلال it (يمكن إعادة تسميته للوضوح) وتعيد سلسلة منسقة. إذا لم يتم العثور على المستخدم، عامل Elvis ?: يعطي قيمة افتراضية. في المثال الثاني let يحول سلسلة نصية إلى رقم مع احتمال null.
run هو نظير let مع الوصول من خلال this، ويعيد نتيجة lambda. run مناسب للحسابات التي تتطلب سياق الكائن ولكنها تعيد قيمة جديدة. استدعاء run() بدون كائن (مثل run { ... }) ينشئ نطاقًا مؤقتًا للمتغيرات. with هي غير امتداد مع الوصول من خلال this، مناسبة لتجميع الاستدعاءات دون سلسلة.
// 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 على قائمة يعيد نتيجة التصفية والتحويل — التحويل نفسه، وليس القائمة. with تأخذ StringBuilder كوسيطة (ليس من خلال نقطة) وتعمل داخليًا مع this: append, append, toString. نتيجة with هي السلسلة info. الاختيار بين run و with غالبًا ما يكون ذاتيًا، لكن with مفضلة عندما يتم تمرير الكائن من مكان آخر دون سلسلة.
with تختلف عن دوال النطاق الأخرى في أنها ليست دالة امتداد. تأخذ الكائن كوسيطة أولى و lambda كوسيطة ثانية. داخل lambda يكون الكائن متاحًا من خلال this. with مناسبة لتجميع استدعاءات دوال الكائن دون حفظ النتيجة أو عندما تحتاج إلى “الدخول” مؤقتًا إلى سياق لقراءة عدة خصائص.
val user = User("Alice", "alice@example.com", "Admin")
val summary = with(user) {
"User $name has role $role and email $email"
}
println(summary)
// with لتهيئة مكونات واجهة المستخدم
with(TextView(context)) {
text = "Hello"
textSize = 18.0f
setTextColor(Color.BLUE)
}
في المثال الأول with يشكل السلسلة summary بالوصول إلى خصائص user مباشرة من خلال this. بدون with كان سيتعين كتابة "User ${user.name} has role ${user.role}" — تكرار user. في المثال الثاني with تجمع تكوين TextView: جميع الدوال والخصائص تُستدعى على كائن واحد دون تكرار. عيب with — عدم القدرة على استخدامها في سلسلة، لأنها ليست امتدادًا ولا تعيد كائن السياق.
الأسئلة الشائعة
استخدم apply — تعيد الكائن وتوفر الوصول من خلال this، مما يسمح بتعيين الخصائص دون تكرار اسم الكائن. also مناسبة إذا كنت بحاجة إلى معامل it صريح للتمييز عن خصائص الكائن.
let تمرر السياق من خلال it (معامل صريح)، run — من خلال this (مستقبل ضمني). let مناسبة لفحوصات null والتحويلات، run — للحسابات مع السياق. كلتاهما تعيدان نتيجة lambda.
with ليست امتدادًا — تأخذ الكائن كوسيطة. استخدم with لتجميع الاستدعاءات عندما لا تكون هناك حاجة لسلسلة. apply هي امتداد يعيد الكائن، مناسب في المهيآت وأنماط builder.
نعم، لكن كن حذرًا مع this — this المتداخل يسبب ارتباكًا. استخدم let أو also بأسماء صريحة للسياقات المتداخلة لتجنب الغموض عند الإشارة إلى الكائن الخارجي.
جميع دوال النطاق هي inline، لذلك لا تنشئ عبئًا من الفئات المجهولة. الفرق في الأداء بينها ضئيل. يجب أن يعتمد الاختيار على قابلية القراءة والاتفاقيات، وليس على السرعة.
الخلاصة
سنقوم بتطوير تطبيق جوال جاهز
تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.
اقرأ أيضًا