Lambda هي دالة مجهولة ممثلة بتعبير مضغوط { وسائط -> جسم }. في Kotlin، تعتبر lambdas الطريقة الأساسية لتمرير السلوك إلى دوال الترتيب الأعلى، مما يوفر بناء جملة موجز دون الحاجة لتعريف دالة منفصلة. وفقًا لتوثيق Kotlin (2026)، أكثر من 70% من دوال الترتيب الأعلى في المشاريع النموذجية تُستدعى مع lambdas. Lambda تتيح كتابة الكود بأسلوب وظيفي مع أقل قدر من الضوضاء التركيبية.
النقاط الرئيسية
Lambda (تعبير lambda) هي دالة مجهولة تُعرف كتعبير بدلاً من تعريف. المصطلح يأتي من حساب اللامدا لألونزو تشرتش (1936). في البرمجة، lambda هي كتلة من الكود يمكن تمريرها كقيمة، تخزينها في متغير واستدعاؤها لاحقًا. في Kotlin، lambda دائمًا محصورة بين أقواس مجعدة { }، مع فصل الوسائط عن الجسم بواسطة السهم ->.
تختلف lambda عن الدالة العادية بغياب الاسم والكلمة المفتاحية fun. هذا يسمح بتعريف السلوك مباشرة في نقطة الاستخدام، دون إرباك الكود بتعريفات إضافية. يقوم مترجم Kotlin باستنتاج أنواع الوسائط من السياق، لذلك في كثير من الحالات يمكن حذف الأنواع. lambda هي سكر نحوي فوق النوع الوظيفي (T) -> R: كل lambda لها نوع وظيفي مقابل.
الاستخدام الرئيسي للـ lambdas في Kotlin هو تمريرها إلى دوال الترتيب الأعلى. تستخدم المكتبة القياسية lambdas في كل مكان: filter { it > 0 }، map { it.uppercase() }، forEach { println(it) }. تُستخدم lambdas أيضًا لمستمعي الأحداث، الاستدعاءات الخلفية ومُهيئات الكائنات عبر apply و run، مما يجعل الكود أكثر تصريحية مقارنة بالفئات المجهولة.
الشكل الكامل لتعبير lambda في Kotlin: { وسائط -> جسم_lambda }. تُدرج الوسائط مفصولة بفواصل، يليها سهم ثم الجسم — تعبير واحد أو أكثر. إذا كان الجسم يحتوي على عدة أسطر، يصبح آخر تعبير هو القيمة المُعادة. إذا كانت الدالة تقبل lambda كآخر وسيطة، يمكن إخراجها خارج أقواس الاستدعاء.
val sum: (Int, Int) -> Int = { a, b -> a + b }
// الأنواع المستنتجة من السياق
val sumShort = { a: Int, b: Int -> a + b }
// استدعاء Lambda
val result = sum(5, 3) // 8
println(result)
المتغير sum يُعلن بنوع وظيفي صريح (Int, Int) -> Int ويُهيأ بـ lambda { a, b -> a + b }. بالنسبة لـ sumShort، الأنواع محددة داخل lambda ونوع المتغير مستنتج. كلا الخيارين متكافئان. استدعاء lambda لا يختلف عن استدعاء دالة عادية — باستخدام بناء sum(5, 3). lambda بدون وسائط تُكتب كـ { body }، بوسيطة واحدة — { it -> it * 2 } أو ببساطة { it * 2 }.
عندما تقبل lambda وسيطة واحدة بالضبط، يوفر Kotlin الاسم الضمني it. هذا يلغي الحاجة لتعريف الوسيطة في الجانب الأيسر. يكتب المطور فقط جسم lambda، مستخدمًا it للوصول إلى المعامل الوحيد. هذا التركيب مناسب بشكل خاص في سلاسل معالجة المجموعات، حيث يشير it إلى العنصر الحالي.
val numbers = listOf(1, 2, 3, 4, 5)
// اسم المعامل الصريح
val squaredExplicit = numbers.map { n -> n * n }
// it ضمني
val squared = numbers.map { it * it }
// التصفية باستخدام it
val even = numbers.filter { it % 2 == 0 }
println(squared) // [1, 4, 9, 16, 25]
في المثال، يستخدم map { it * it } it بدلاً من تعريف n ->. يقوم Kotlin تلقائيًا بتعيين it لمعامل lambda الوحيد. إذا كانت lambda تقبل وسيطتين أو أكثر، لا يتم إنشاء it — يجب تعريف جميع المعاملات بشكل صريح. لتحسين قراءة lambdas المتداخلة، من الأفضل إعطاء المعاملات أسماء ذات معنى واستخدام it فقط في المسندات القصيرة المكونة من سطر واحد.
Trailing lambda هي ميزة نحوية في Kotlin تسمح بإخراج آخر وسيطة lambda خارج أقواس استدعاء الدالة. هذا يجعل الكود يشبه تركيبات اللغة المضمنة. إذا كانت lambda هي الوسيطة الوحيدة، يمكن حذف الأقواس بالكامل. يُستخدم هذا الأسلوب بنشاط في المكتبة القياسية والـ DSLs.
// استدعاء قياسي
thread(block = {
println("مرحبًا من السلسلة")
})
// إصدار trailing lambda
thread {
println("مرحبًا من السلسلة")
}
// تكرار مع trailing lambda
repeat(3) { index ->
println("تكرار $index")
}
// تشغيل مع lambda متعددة الأسطر
val config = run {
val host = "localhost"
val port = 8080
"$host:$port"
}
الدالة thread تقبل Runnable كآخر وسيطة. بفضل trailing lambda، يمكن تمرير lambda خارج الأقواس، مما يشبه بصريًا كتلة كود. الدالة repeat تأخذ عدد التكرارات و lambda مع فهرس. run هي مثال لدالة ترتيب أعلى حيث lambda متعددة الأسطر تحسب قيمة يُرجعها آخر تعبير. يمكن حذف الأقواس حول run() إذا كانت lambda هي الوسيطة الوحيدة.
التقاط المتغيرات (closure) هو قدرة lambda على الوصول إلى المتغيرات المُعلنة في النطاق الخارجي. في Kotlin، يمكن لـ lambda قراءة وتعديل متغيرات var، وكذلك قراءة متغيرات val من السياق المحيط. تُخزن المتغيرات الملتقطة مع lambda طوال دورة حياتها، مما يتيح إنشاء دوال بحالة.
fun makeCounter(): () -> Int {
var count = 0
return {
count++ // عدد مرات الالتقاط
}
}
fun main() {
val counter = makeCounter()
println(counter()) // 0
println(counter()) // 1
println(counter()) // 2
}
الدالة makeCounter تُعلن متغيرًا محليًا count وتُعيد lambda { count++ }. على الرغم من أن makeCounter أكملت تنفيذها، تحتفظ lambda المُعادة بمرجع إلى count عبر الإغلاق. كل استدعاء لـ counter() يزيد ويعيد القيمة الحالية. آلية الإغلاق تكمن وراء الكاري، مصانع الدوال والتخزين المؤقت. من المهم تذكر أن التقاط المتغيرات القابلة للتغيير قد يؤدي إلى سلوك غير متوقع في التنفيذ المتزامن.
الأسئلة الشائعة
Lambda لا تدعم return غير المشروط — return بدون علامة يُعيد التحكم إلى الدالة الخارجية. للخروج من lambda، استخدم return@label، حيث label هو اسم دالة الترتيب الأعلى التي مُررت إليها lambda.
الدالة المجهولة تُكتب كـ fun(a: Int): Int { return a * 2 } وتدعم return بدون علامة. lambda { a -> a * 2 } أقصر في بناء الجملة ولكنها تتطلب return@label للخروج المبكر.
إذا كانت lambda لا تقبل وسائط، يُحذف السهم: { body() }. إذا كانت تقبل وسيطة واحدة ولا تستخدمها، اكتب { _ -> body() } بتسطير لتجاهل المعامل.
نعم، كل lambda تُمرر إلى دالة غير inline تنشئ مثيلاً لفئة مجهولة. بالنسبة للدوال inline، تُضمّن lambda في موقع الاستدعاء دون إنشاء كائن. lambdas غير الملتقطة تُخزن مؤقتًا في كائنات منفردة.
Return في Kotlin هو عامل قفز ينتمي إلى أقرب دالة مُعلنة بـ fun. lambda ليست دالة (fun)، بل تعبير. لذلك، return داخل lambda بدون علامة يُعتبر عودة من الدالة الخارجية المحيطة.
الخلاصة
سنقوم بتطوير تطبيق جوال جاهز
تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.
اقرأ أيضًا