Kotlin: دوال كوتلن وتعبيرات Lambda

آخر تحديث: 2026-08-26

الدوال هي مواطنات من الدرجة الأولى في كوتلن — الدوال الرتبة الأعلى + lambdas + inline تتيح لتشارلي بناء خطوط أنابيب معالجة بيانات OrderProcessor بأسلوب وظيفي، مما يقلل الكود بنسبة 60%.

1. ما ستتعلمه


2. قصة مطور حقيقية

(1) مشكلة: منطق معالجة طلبات مكرر

في فريق تشارلي، كل مطور كتب منطق التصفية/التحويل/التجميع الخاص به — 100+ سطر من الكود المكرر منتشر عبر 5 خدمات. تغيير شرط تصفية واحد يعني التغيير في 5 أماكن.

(2) الدوال الرتبة الأعلى تقضي على التكرار

KOTLIN
// معالج طلبات عام - دالة واحدة تحل محل 5 نسخ
fun <T> processOrders(
    orders: List<Order>,
    filter: (Order) -> Boolean,
    transform: (Order) -> T
): List<T> = orders.filter(filter).map(transform)

// الاستخدام: كل موقع استدعاء سطران بدلاً من 20
val highValueIds = processOrders(orders, { it.total > 1_000 }, { it.id })
val vipCustomers = processOrders(orders, { it.total > 10_000 }, { it.customer })

الدوال الرتبة الأعلى تجعل "ما يتغير" معلمات، لذا "ما يبقى ثابتًا" يُكتب مرة واحدة فقط.


3. تعريفات الدوال

(1) الدوال الأساسية

KOTLIN
// جسم كتلة
fun calculateTax(total: Double, rate: Double = 0.08): Double {
    return total * rate
}

// جسم تعبير
fun calculateTax(total: Double, rate: Double = 0.08) = total * rate

// إرجاع Unit (void)
fun logOrder(order: Order) {
    println("Processing ${order.id}")
}

(2) المعلمات الافتراضية والوسائط المسماة

KOTLIN
// المعلمات الافتراضية - تقضي على تحميل الطرق
fun createOrder(
    id: String,
    total: Double,
    status: String = "PENDING",
    currency: String = "USD",
    taxRate: Double = 0.08
): Order = Order(id, total, status, currency)

// وسائط مسماة - استدعِ أي معلمة بالاسم
val order1 = createOrder("ORD-001", 299.99)
val order2 = createOrder("ORD-002", 1_500.00, taxRate = 0.10)
val order3 = createOrder("ORD-003", 45.50, currency = "EUR", status = "CONFIRMED")

(3) المعلمات الافتراضية مقابل تحميل الطرق في جافا

البعد تحميل جافا المعلمات الافتراضية في كوتلن
حجم الكود N طريقة طريقة واحدة
القابلية للصيانة تغيير N أماكن لمعامل واحد تغيير مكان واحد
مرونة الاستدعاء ترتيب موضعي فقط وسائط مسماة، أي تركيبة
التوافق جافا تستدعي مباشرة يحتاج @JvmOverloads

4. الدوال الرتبة الأعلى

الدوال الرتبة الأعلى تأخذ دوال كمعلمات أو تُرجع دوال — حجر الأساس للبرمجة الوظيفية في كوتلن.

(1) أنواع الدوال

KOTLIN
// بنية نوع الدالة
val transform: (Order) -> String = { order -> order.id }
val predicate: (Order) -> Boolean = { it.total > 1_000 }
val aggregator: (Double, Order) -> Double = { acc, order -> acc + order.total }
val supplier: () -> Order = { Order("DEFAULT", 0.0, "PENDING") }

// الدالة كمعامل
fun processOrders(orders: List<Order>, transform: (Order) -> String): List<String> {
    return orders.map(transform)
}

(2) الدالة كقيمة إرجاع

KOTLIN
// إرجاع دالة
fun getDiscountStrategy(tier: String): (Double) -> Double = when (tier) {
    "VIP" -> { total -> total * 0.80 }       // خصم 20%
    "GOLD" -> { total -> total * 0.90 }      // خصم 10%
    else -> { total -> total }               // بدون خصم
}

val discount = getDiscountStrategy("VIP")
val finalPrice = discount(1_000.00)  // 800.0

(3) دالة رتبة أعلى + سلسلة استدعاء lambda

100%
flowchart LR
    A[Orders] --> B[filter]
    B --> C[map]
    C --> D[sortedBy]
    D --> E[fold]
    E --> F[Result]

5. تعبيرات Lambda

(1) بنية lambda

KOTLIN
// بنية كاملة
val total = orders.map({ order: Order -> order.total })

// استدلال النوع
val total = orders.map({ order -> order.total })

// كلمة it للمعامل الواحد
val total = orders.map({ it.total })

// اصطلاح lambda الخلفي
val total = orders.map { it.total }

// lambda متعددة المعاملات
val result = orders.fold(0.0) { acc, order -> acc + order.total }

(2) مستويات تبسيط بنية lambda

المستوى البنية الوصف
كاملة { order: Order -> order.total } نوع صريح
مبسطة 1 { order -> order.total } استدلال النوع
مبسطة 2 { it.total } معامل واحد مع it
lambda خلفي orders.map { it.total } lambda خارج الأقواس

(3) أنماط lambda الشائعة

KOTLIN
// let: تحويل واستهلاك
val formatted = order.total.let { "\$$it USD" }

// also: تأثير جانبي دون تغيير القيمة
val order = createOrder("ORD-001", 299.99).also {
    println("Created order: ${it.id}")
}

// apply: تهيئة الكائن
val order = Order("ORD-001", 0.0, "PENDING").apply {
    // يمكن استخدامه فقط مع خصائص var
}

// run: تنفيذ كتلة وإرجاع النتيجة
val summary = order.run {
    "Order $id: \$${total} USD ($status)"
}

6. اصطلاح lambda الخلفي

عندما يكون المعامل الأخير للدالة lambda، يمكن كتابته خارج الأقواس — هذه أهم سكر بنوي في كوتلن.

(1) الاستخدام الأساسي

KOTLIN
// التعريف
fun processOrders(orders: List<Order>, callback: (Order) -> Unit) { ... }

// الاستدعاء: lambda خلفي
processOrders(orders) { order ->
    println(order.id)
}

// إذا كان lambda المعامل الوحيد، احذف الأقواس
orders.forEach { println(it.id) }

(2) lambda الخلفي كأساس لـ DSLs

KOTLIN
// هكذا تعمل DSLs
fun order(id: String, init: OrderBuilder.() -> Unit): Order {
    val builder = OrderBuilder(id)
    builder.init()
    return builder.build()
}

// الاستخدام: lambda الخلفي يجعله يُقرأ كتهيئة
val myOrder = order("ORD-001") {
    total(299.99)
    status("CONFIRMED")
    customer("Alice")
}

7. الدوال المضمنة

تُترجَل lambdas إلى كائنات فئات مجهولة على JVM، مما ينشئ نسخًا جديدة في كل استدعاء. inline يقضي على هذه التكلفة.

(1) أساسيات inline

KOTLIN
// بدون inline: Lambda ينشئ كائنًا في كل استدعاء
fun process(order: Order, block: (Order) -> String): String {
    return block(order)
}

// مع inline: كود Lambda يُدرج في موقع الاستدعاء
inline fun process(order: Order, block: (Order) -> String): String {
    return block(order)
}

(2) مقارنة تأثير inline

KOTLIN
// بعد الإدراج، هذا:
process(order) { it.id }

// يصبح (مفهوميًا):
run { order.id }

(3) inline مقابل noinline مقابل crossinline

المُعدِّل التأثير حالة الاستخدام
inline كود lambda مُدرج في موقع الاستدعاء Lambdas المستدعاة بشكل متكرر
noinline يمنع الإدراج (يبقى ككائن) lambda يحتاج للتخزين/التمرير
crossinline مُدرج لكن يمنع الإرجاع غير المحلي lambda في سياق تنفيذ مختلف

(4) معاملات النوع reified

KOTLIN
// عادي: النوع يُمحى في وقت التشغيل
fun <T> isType(value: Any): Boolean = value is T  // خطأ

// reified: inline يحافظ على معلومات النوع
inline fun <reified T> isType(value: Any): Boolean = value is T  // موافق

isType<String>("Hello")  // true
isType<Int>(42)          // true
isType<Int>("42")        // false

8. مثال كامل: خط أنابيب وظيفي لـ OrderProcessor

KOTLIN
// ============================================
// OrderProcessor - خط أنابيب وظيفي
// الميزة: معالجة الطلبات بدوال رتبة أعلى
// ============================================

data class Order(val id: String, val total: Double, val status: String, val customer: String)

// معالج خط أنابيب عام
inline fun <T, R> List<T>.pipeline(
    filter: (T) -> Boolean,
    transform: (T) -> R,
    crossinline postProcess: (R) -> Unit
): List<R> {
    return this.filter(filter).map(transform).also { results ->
        results.forEach(postProcess)
    }
}

// مصنع استراتيجية الخصم
fun getDiscountStrategy(tier: String): (Double) -> Double = when (tier) {
    "VIP" -> { total -> total * 0.80 }
    "GOLD" -> { total -> total * 0.90 }
    "SILVER" -> { total -> total * 0.95 }
    else -> { total -> total }
}

fun main() {
    val orders = listOf(
        Order("ORD-001", 299.99, "CONFIRMED", "Alice"),
        Order("ORD-002", 15_000.00, "CONFIRMED", "Bob"),
        Order("ORD-003", 2_500.00, "PENDING", "Charlie"),
        Order("ORD-004", 45.50, "CANCELLED", "Alice"),
        Order("ORD-005", 8_000.00, "CONFIRMED", "Bob")
    )

    // خط الأنابيب: تصفية المؤكدة → تطبيق خصم VIP → تسجيل
    val discount = getDiscountStrategy("VIP")
    val processed = orders.pipeline(
        filter = { it.status == "CONFIRMED" },
        transform = { it.copy(total = discount(it.total)) },
        postProcess = { println("  Processed: ${it.id} -> \$${it.total} USD") }
    )

    // تجميع النتائج
    val totalRevenue = processed.sumOf { it.total }
    val avgOrder = processed.map { it.total }.average()
    println("\nRevenue: \$$totalRevenue USD | Avg: \$${"%.2f".format(avgOrder)} USD")

    // تركيب وظيفي مع let/also/run
    val report = orders
        .filter { it.status != "CANCELLED" }
        .groupBy { it.customer }
        .mapValues { (_, list) ->
            list.sumOf { it.total }
        }
        .also { println("\nRevenue by customer:") }
        .map { (customer, total) -> "$customer: \$$total USD" }
        .joinToString("\n  ")

    println("  $report")
}

المخرجات:

TEXT 📖 للعرض فقط
  Processed: ORD-001 -> $239.992 USD
  Processed: ORD-002 -> $12000.0 USD
  Processed: ORD-005 -> $6400.0 USD

Revenue: $18639.992 USD | Avg: $6213.330666666667 USD

Revenue by customer:
  Alice: $299.99 USD
  Bob: $23000.0 USD
  Charlie: $2500.0 USD

9. أمثلة عملية سريعة

▶ مثال: دوال أساسية

KOTLIN
// دالة بدون قيمة مرجعة
fun greet(name: String): Unit {
    println("Hello, $name!")
}

// دالة بقيمة مرجعة
fun add(a: Int, b: Int): Int {
    return a + b
}

// دالة بجسم تعبير
fun multiply(a: Int, b: Int) = a * b

// معامل افتراضي
fun greetWithPrefix(name: String, prefix: String = "Mr.") {
    println("$prefix $name")
}

greet("Alice")
println("Add: ${add(3, 5)}")
println("Multiply: ${multiply(4, 7)}")
greetWithPrefix("Bob")
greetWithPrefix("Charlie", "Dr.")

**المخرجات:

TEXT 📖 للعرض فقط
Hello, Alice!
Add: 8
Multiply: 28
Mr. Bob
Dr. Charlie

▶ مثال: المعاملات المسماة والاختيارية

KOTLIN
fun createOrder(
    id: String,
    items: List<String>,
    urgent: Boolean = false,
    notes: String? = null
): String {
    val status = if (urgent) "URGENT" else "NORMAL"
    val noteStr = notes?.let { " [$it]" } ?: ""
    return "$id ($status)$noteStr: ${items.joinToString(", ")}"
}

// معاملات موضعية
println(createOrder("ORD-001", listOf("كتاب", "قلم")))

// معاملات مسماة (ترتيب مرن)
println(createOrder(urgent = true, id = "ORD-002", items = listOf("حاسوب")))

// معامل اختياري
println(createOrder("ORD-003", listOf("هاتف"), notes = "هش"))

**المخرجات:

TEXT 📖 للعرض فقط
ORD-001 (NORMAL): كتاب, قلم
ORD-002 (URGENT): حاسوب
ORD-003 (NORMAL) [هش]: هاتف

▶ مثال: Lambda الأساسية

KOTLIN
// lambda بسيط
val greet: (String) -> Unit = { name -> println("Hello, $name!") }
greet("Alice")

// lambda مع معامل واحد → كلمة it ضمنية
val double: (Int) -> Int = { it * 2 }
println("Double 5: ${double(5)}")

// lambda مع معاملات متعددة
val sum: (Int, Int) -> Int = { a, b -> a + b }
println("Sum 3+7: ${sum(3, 7)}")

// lambda مع نوع إرجاع صريح
val divide: (Double, Double) -> Double = { a, b ->
    if (b == 0.0) 0.0 else a / b
}
println("10/2: ${divide(10.0, 2.0)}")
println("10/0: ${divide(10.0, 0.0)}")

**المخرجات:

TEXT 📖 للعرض فقط
Hello, Alice!
Double 5: 10
Sum 3+7: 10
10/2: 5.0
10/0: 0.0

▶ مثال: دوال ذات معاملات lambda

KOTLIN
// دالة تقبل lambda كمعامل
fun operate(a: Int, b: Int, operation: (Int, Int) -> Int): Int {
    return operation(a, b)
}

// تمرير lambda
val result1 = operate(10, 5, { x, y -> x + y })
println("10 + 5 = $result1")

val result2 = operate(10, 5) { x, y -> x * y }  // lambda خارج الأقواس
println("10 * 5 = $result2")

// دالة مخصصة تأخذ lambda
fun List<Int>.customFilter(predicate: (Int) -> Boolean): List<Int> {
    val result = mutableListOf<Int>()
    for (item in this) {
        if (predicate(item)) result.add(item)
    }
    return result
}

val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
val evens = numbers.customFilter { it % 2 == 0 }
val bigOnes = numbers.customFilter { it > 5 }
println("Evens: $evens")
println("Big ones: $bigOnes")

**المخرجات:

TEXT 📖 للعرض فقط
10 + 5 = 15
10 * 5 = 50
Evens: [2, 4, 6, 8, 10]
Big ones: [6, 7, 8, 9, 10]

▶ مثال: Trailing lambda و it الضمني

KOTLIN
val names = listOf("Alice", "Bob", "Charlie", "Dave")

// forEach مع it ضمني
names.forEach { println("Hello, $it!") }

// filter مع شرط
val longNames = names.filter { it.length > 4 }
println("Long names: $longNames")

// map مع تحويل
val upper = names.map { it.uppercase() }
println("Uppercase: $upper")

// سلسلة عمليات
val result = names
    .filter { it.startsWith("A") || it.startsWith("C") }
    .map { "Mr. $it" }
    .sorted()
println("Filtered chain: $result")

// lambda مع معاملين واسمين
val pairs = listOf(1 to "one", 2 to "two", 3 to "three")
pairs.forEach { (num, str) ->
    println("$num = $str")
}

**المخرجات:

TEXT 📖 للعرض فقط
Hello, Alice!
Hello, Bob!
Hello, Charlie!
Hello, Dave!
Long names: [Alice, Charlie]
Uppercase: [ALICE, BOB, CHARLIE, DAVE]
Filtered chain: [Mr. Alice, Mr. Charlie]
1 = one
2 = two
3 = three

▶ مثال: Function references و inline

KOTLIN
// مرجع دالة بـ ::
val numbers = listOf(-3, -1, 2, 5, -7, 8, 0)
val positives = numbers.filter { it > 0 }
val positivesViaRef = numbers.filter(::isPositive)

fun isPositive(n: Int): Boolean = n > 0

println("Positives: $positives")
println("Via reference: $positivesViaRef")

// inline function - تحسين الأداء عند استخدام lambda
inline fun measureTime(action: () -> Unit): Long {
    val start = System.nanoTime()
    action()
    return System.nanoTime() - start
}

val elapsed = measureTime {
    // محاكاة عمل ما
    var sum = 0
    for (i in 1..100000) sum += i
    println("Sum 1-100000: $sum")
}
println("Elapsed: ${elapsed / 1_000_000} ms")

**المخرجات:

TEXT 📖 للعرض فقط
Positives: [2, 5, 8]
Via reference: [2, 5, 8]
Sum 1-100000: 705082704
Elapsed: 0 ms

▶ مثال: Higher-order functions مخصصة

KOTLIN
// دالة repeat تنفيذ N مرة
inline fun repeat(times: Int, action: (Int) -> Unit) {
    for (i in 0 until times) action(i)
}

println("---")
repeat(3) { i -> println("Iteration $i") }

// دالة use للأمان مع الموارد
inline fun <T> use(resource: T, block: (T) -> Unit) {
    try {
        block(resource)
    } finally {
        println("Cleaning up resource")
    }
}

data class Connection(val name: String) {
    fun execute(query: String) = println("[$name] Executing: $query")
}

use(Connection("DB-1")) { conn ->
    conn.execute("SELECT *")
    conn.execute("UPDATE")
}

**المخرجات:

TEXT 📖 للعرض فقط
---
Iteration 0
Iteration 1
Iteration 2
[DB-1] Executing: SELECT *
[DB-1] Executing: UPDATE
Cleaning up resource

❓ أسئلة شائعة

س ما الفرق بين lambda والدالة المجهولة؟
ج lambdas تستخدم it كمعامل ضمني وreturn يتصرف بشكل مختلف (إرجاع return في lambda غير محلي، إرجاع return في الدالة المجهولة محلي). استخدم lambdas في معظم الحالات.
س متى يجب أن أستخدم it مقابل تسمية صريحة؟
ج استخدم it عندما يكون جسم lambda 1-2 سطر والسياق واضح؛ استخدم أسماء صريحة للأجسام الأطول أو lambdas المتداخلة لتحسين القراءة.
س هل يجب أن تكون جميع الدوال الرتبة الأعلى inline؟
ج لا. inline أفضل للأجسام الصغيرة حيث يُستدعى lambda مباشرة. إدراج الدوال الكبيرة يزيد حجم الكود. معظم دوال المكتبة القياسية الرتبة الأعلى هي inline.
س هل يمكن استخدام reified في الفئات العادية؟
ج لا. reified يعمل فقط على معاملات نوع الدوال inline، لأن الإدراج يسمح للمُترجِم بحفظ معلومات النوع في موقع الاستدعاء. معاملات نوع الدوال العادية تُمحى في وقت التشغيل.
س هل اصطلاح lambda الخلفي مجرد سكر بنوي؟
ج هو سكر بنوي لكنه بالغ الأهمية — إنه أساس بناء Kotlin DSL. Gradle Kotlin DSL، Compose UI، Anko، إلخ جميعها تعتمد على هذا الاصطلاح.
س كيف أختار بين let/also/apply/run؟
ج let لتحويل القيم، also للتأثيرات الجانبية، apply لتهيئة الكائنات (يُرجع this)، run لتنفيذ كتلة (يُرجع النتيجة). تذكر: apply يُرجع this، run يُرجع نتيجة lambda.

📖 ملخص


📝 تمارين

  1. أساسي (⭐): اكتب دالة calculateTotal بمعلمات افتراضية واستدعِها بوسائط مسماة. تلميح: fun calc(price: Double, tax: Double = 0.08)
  2. متوسط (⭐⭐): نفّذ processAndTransform بدوال رتبة أعلى، تقبل شرط تصفية ودالة تحويل، وتُرجع قائمة نتائج. تلميح: <T> fun List<T>.process...
  3. تحدي (⭐⭐⭐): نفّذ دالة filterByType<Any> ترشّح عناصر نوع محدد من قائمة أنواع مختلطة. تلميح: inline fun <reified T> List<Any>.filterByType(): List<T>

← السابق | التالي ←

Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%