تخطَّ إلى المحتوى

الدوال في Kotlin: fun والمعاملات و Lambda

الدرس 5 من 11 · Kotlin

الدالة مجموعة أوامر لها اسم، تكتبها مرة وتستدعيها متى شئت. والدوال في Kotlin مرنة جداً: يمكن كتابتها في سطر واحد، ومعاملاتها يمكن أن تكون لها قيم افتراضية وأسماء، ويمكن تمريرها كقيم لدوال أخرى. هذه المرونة تجعل كود أندرويد الحديث، وخاصة واجهات Jetpack Compose، قصيراً وواضحاً.

fun greet() {
println("أهلاً بك في Kotlin!")
}
fun add(a: Int, b: Int): Int {
return a + b
}
fun main() {
greet()
println(add(3, 4))
}
الناتج
أهلاً بك في Kotlin!
7
  • fun: الكلمة التي تبدأ بها أي دالة.
  • a: Int, b: Int: المعاملات، ولكل معامل نوعه إجبارياً.
  • : Int بعد القوسين: نوع القيمة المرجعة.
  • الدالة التي لا تُرجع شيئاً مفيداً، مثل greet، نوعها Unit، ولا نكتبه عادة.

الدوال ذات التعبير الواحد

Section titled “الدوال ذات التعبير الواحد”

إذا كانت الدالة تُرجع تعبيراً واحداً، اختصرها بعلامة =:

fun square(x: Int) = x * x
fun isEven(n: Int) = n % 2 == 0

لاحظ أننا لم نكتب نوع القيمة المرجعة: Kotlin تستنتجه. square(5) تُرجع 25، و isEven(4) تُرجع true.

يمكن أن تعطي المعامل قيمة تُستخدم إذا لم يرسل المستدعي قيمة:

fun makeTea(sugar: Int = 2): String = "شاي بـ $sugar ملاعق سكر"
println(makeTea())
println(makeTea(0))
الناتج
شاي بـ 2 ملاعق سكر
شاي بـ 0 ملاعق سكر

عند الاستدعاء، تستطيع كتابة اسم كل معامل، فيصبح الكود مقروءاً مثل الجملة، ولا يهم الترتيب:

fun createUser(name: String, age: Int, city: String = "القاهرة"): String {
return "$name ($age سنة) من $city"
}
println(createUser("عمر", 15))
println(createUser(age = 20, name = "منى", city = "جدة"))
الناتج
عمر (15 سنة) من القاهرة
منى (20 سنة) من جدة

ما فائدة ذلك؟ قارن: createUser("منى", 20, "جدة") مقابل createUser(name = "منى", age = 20, city = "جدة"). الثانية أوضح بكثير، خاصة عندما تكثر المعاملات أو تتشابه أنواعها. مثل الاستمارة التي مكتوب بجانب كل خانة اسمها.

في Kotlin، الدالة قيمة تستطيع وضعها في متغير، أو تمريرها لدالة أخرى. والدالة بلا اسم تكتب بين قوسين معقوفين وتسمى Lambda:

val double = { x: Int -> x * 2 }
println(double(21))
الناتج
42
  • قبل السهم -> المعاملات، وبعده الكود.
  • آخر سطر في الـ Lambda هو قيمتها المرجعة تلقائياً.

وأجمل استخدام لها مع القوائم:

val numbers = listOf(1, 2, 3)
println(numbers.map { it * 10 })
numbers.forEach { println("الرقم $it") }
الناتج
[10, 20, 30]
الرقم 1
الرقم 2
الرقم 3

لاحظ شيئين:

  • عندما يكون للـ Lambda معامل واحد، لا داعي لتسميته؛ يُسمى تلقائياً it.
  • عندما تكون الـ Lambda آخر معامل للدالة، تُكتب خارج القوسين: map { ... } بدلاً من map({ ... }).

هذا الأسلوب هو أساس Jetpack Compose في أندرويد، حيث تُبنى الواجهات بدوال تستقبل Lambda مثل: Button(onClick = { ... }) { Text("اضغط") }.

اكتب دالة تحسب السعر بعد الخصم، مع نسبة افتراضية 10:

fun priceAfterDiscount(price: Double, percent: Double = 10.0) =
price - price * percent / 100
fun main() {
println(priceAfterDiscount(200.0))
println(priceAfterDiscount(200.0, percent = 25.0))
}
الناتج المتوقع
180.0
150.0

تحدٍّ: اكتب دالة grade(score: Int): String تستخدم when بدون قيمة لتُرجع «ممتاز» أو «ناجح» أو «راسب»، ثم استخدم map لتحويل قائمة درجات إلى قائمة تقديرات.

  • نسيان نوع المعامل: fun add(a, b) خطأ. كل معامل يحتاج نوعه: a: Int.
  • نسيان return في الدالة ذات الأقواس المعقوفة: إذا أعلنت نوعاً مرجعاً مثل : Int، يجب أن تُرجع قيمة بـ return.
  • محاولة تغيير المعامل داخل الدالة: المعاملات في Kotlin تُعامل مثل val، فلا يمكن إعادة إسنادها. انسخها في متغير var إن احتجت.

سؤال

ما ناتج listOf(1, 2, 3).map { it + 1 }؟

سؤال

أي استدعاء صحيح للدالة fun order(item: String, qty: Int = 1)؟

  • الدالة تبدأ بـ fun، ولكل معامل نوعه، ونوع القيمة المرجعة بعد القوسين.
  • fun square(x: Int) = x * x اختصار للدالة ذات التعبير الواحد.
  • القيم الافتراضية والمعاملات المسماة تجعل الاستدعاء مرناً وواضحاً.
  • Lambda دالة مجهولة بين { }، والمعامل الوحيد اسمه it تلقائياً.
  • الـ Lambda الأخيرة تُكتب خارج القوسين، وهذا أساس أسلوب Jetpack Compose.