Destructuring declaration — ما هو، بناء الجملة والاستخدام في Kotlin

المؤلف: IT Sectr نُشر: 2026-06-23 وقت القراءة: 7 دق

Destructuring declaration هي ميزة في Kotlin تتيح لك تحليل كائن إلى عدة متغيرات في تعبير واحد. البناء val (name, age) = user يعلن في نفس الوقت عن متغيرين، مع تعيين قيم من مكونات الكائن. وفقًا لوثائق Kotlin (2026)، يقلل التفكيك من حجم الكود عند العمل مع data classes بنسبة 30–50%. Destructuring declaration يجعل الكود أكثر إيجازًا وقابلية للقراءة.

الملامح الرئيسية

  • Destructuring declaration — بناء val (a, b) = obj لتحليل كائن إلى متغيرات
  • ComponentN() — دوال component1() و component2() و component3() وهكذا، تُستدعى تلقائيًا
  • Data class تُنشئ تلقائيًا componentN() لجميع الخصائص المعلنة في المنشئ الأساسي
  • Pair و Triple — فئات قياسية مع تفكيك مدمج لقيمتين وثلاث قيم
  • Map.Entry — يدعم التفكيك إلى key و value للتكرار الملائم

ما هو destructuring declaration؟

Destructuring declaration (إعلان التفكيك) هو بناء جملة في Kotlin يسمح لك بتحليل نسخة من فئة إلى عدة متغيرات في تعبير واحد. الجانب الأيسر يحتوي على قائمة من المتغيرات بين قوسين، والجانب الأيمن يحتوي على الكائن الذي يتم تحليله. يستدعي Kotlin تلقائيًا الدالة componentN() المقابلة لكل متغير، حيث N هو الرقم الترتيبي للمتغير بين القوسين.

يُستخدم إعلان التفكيك بشكل شائع مع data classes. يُنشئ مترجم Kotlin تلقائيًا component1() و component2() وهكذا لكل data class — بناءً على عدد الخصائص في المنشئ الأساسي. بالنسبة للفئات العادية بدون معدِّل data، لا يتم إنشاء componentN()، ولكن يمكن تعريفها يدويًا كدوال عامل مع الكلمة المفتاحية operator.

أصبح إعلان التفكيك نمطًا شائعًا بفضل قدرته على تبسيط الكود عند العمل مع مجموعات الأزواج ونتائج الدوال وتكرار Map. بدلاً من كتابة person.firstName و person.lastName، يكتب المطور val (firstName, lastName) = person. هذا يقلل عدد الأسطر ويحسن readability، خاصة في الحلقات واللامبدات.

كيف يعمل التفكيك في Kotlin؟

عند مواجهة البناء val (a, b) = obj، يُنشئ مترجم Kotlin الكود التالي: val a = obj.component1(); val b = obj.component2(). إذا تم الإعلان عن المتغيرات باستخدام var، يتم إنشاء تعيينات var. يُستدل على نوع كل متغير من نوع الإرجاع للدالة componentN() المقابلة. هذا يعني أن إعلان التفكيك آمن تمامًا من حيث الأنواع ويتم التحقق منه في وقت الترجمة.

kotlin
data class Person(val firstName: String, val lastName: String, val age: Int)

fun main() {
    val person = Person("Alice", "Smith", 30)

    // التفكيك: component1()، component2()، component3()
    val (firstName, lastName, age) = person
    println("الاسم $firstName واللقب $lastName، العمر $age سنة")
}

في المثال، Person هي data class بثلاث خصائص. يُنشئ Kotlin component1() → firstName و component2() → lastName و component3() → age. التعبير val (firstName, lastName, age) = person يستدعي هذه الدوال ويُسند النتائج. المهم: أسماء المتغيرات لا تحتاج إلى مطابقة أسماء الخصائص — المترجم ينظر فقط إلى ترتيب componentN() وأنواعها. يمكنك تحليل أول N خصائص فقط، متجاهلاً الباقي.

تفكيك data classes والأنواع القياسية

Data classes هي المرشحة الرئيسية لإعلان التفكيك. كل data class تحصل تلقائيًا على componentN() لجميع خصائص المنشئ الأساسي. ترتيب componentN() يتوافق مع ترتيب إعلان الخصائص. هذا يعني أنه عند تغيير ترتيب الحقول في data class، يتغير ترتيب المتغيرات في التفكيك أيضًا — مما قد يؤدي إلى أخطاء خفية.

kotlin
// تفكيك Pair
val pair = Pair("key", 42)
val (key, value) = pair
println("$key -> $value")

// تفكيك Triple
val triple = Triple("A", "B", "C")
val (first, second, third) = triple

// Map.Entry في حلقة
val map = mapOf("a" to 1, "b" to 2)
for ((k, v) in map) {
    println("$k = $v")
}

Pair و Triple هما فئتان قياسيتان مع تفكيك مدمج لمكونين وثلاثة مكونات. يُستخدم Pair غالبًا كنوع إرجاع للدوال التي تحتاج إلى إرجاع قيمتين مرتبطتين دون إنشاء data class منفصلة. في حلقة for على Map.Entry، البناء (k, v) = entry يفكك المفتاح والقيمة عبر component1() و component2(). هذا هو النمط القياسي لتكرار Map في Kotlin، ليحل تمامًا محل الاستدعاءات الصريحة لـ key و value.

الشرطة السفلية للمتغيرات غير المستخدمة

إذا كانت بعض المكونات غير ضرورية أثناء التفكيك، يسمح Kotlin باستبدالها بشرطة سفلية _. هذا يحسن readability — يرى القارئ فورًا أن المكون يتم تجاهله. بالإضافة إلى ذلك، استخدام _ يمنع الوصول العرضي إلى متغير غير مُهيأ: لا يُنشئ المترجم أي متغير لـ _، مما يلغي التحذيرات حول المتغيرات غير المستخدمة.

kotlin
data class Order(
    val id: Long,
    val customerName: String,
    val amount: Double,
    val status: String
)

fun process(orders: List<Order>) {
    for ((id, _, amount) in orders) {
        println("الطلب $id: $amount")
    }
}

في المثال، Order له أربع خصائص، لكن الحلقة تحتاج فقط id و amount. الشرطة السفلية بدلاً من customerName تُخبر المترجم «تجاوز component2()». لا يستدعي Kotlin component2() ولا يُنشئ متغيرًا لهذا المكون. مهم: يمكن استخدام الشرطة السفلية فقط في إعلانات التفكيك، وليس في منشئ data class. إذا تم تخطي عدة مكونات متتالية، يحصل كل منها على شرطته السفلية الخاصة.

إنشاء componentN() للفئات المخصصة

للفئات بدون معدِّل data، يمكنك تعريف componentN() يدويًا بإضافة الكلمة المفتاحية operator. هذا يسمح لك باستخدام إعلان التفكيك مع أي فئة، بما في ذلك فئات الطرف الثالث التي لا يمكن تعديل كودها المصدر — فقط اكتب دالة امتداد مع operator. هذا النهج مفيد بشكل خاص لفئات المكتبات وفئات Java التي لا تحتوي على componentN() تلقائي.

kotlin
class Rect(val width: Int, val height: Int)

operator fun Rect.component1(): Int = width
operator fun Rect.component2(): Int = height

// لفئة Java مع getters
operator fun Dimension.component1(): Int = width
operator fun Dimension.component2(): Int = height

fun main() {
    val rect = Rect(1920, 1080)
    val (w, h) = rect
    println("الدقة: ${w}x${h}")
}

في المثال، Rect هي فئة عادية بدون data. دوال الامتداد مع operator fun component1() = width و component2() = height تُضيف دعم التفكيك. بالمثل بالنسبة لـ java.awt.Dimension: امتداد operator على Dimension يسمح بتحليله إلى width و height. الحد: أقصى عدد من المكونات لإعلان التفكيك هو 5. نظريًا يمكنك تعريف component6() وما فوق، لكن Kotlin لن يُنشئ بناء الجملة المقابل لـ 6+ متغيرات بين قوسين.

الأسئلة الشائعة

هل تدعم كل data class التفكيك؟

نعم، كل data class تحصل تلقائيًا على componentN() لجميع خصائص المنشئ الأساسي. الخصائص المُعلنة في جسم الفئة (باستخدام معاملات المنشئ بدون val/var) لا تُنشئ componentN().

ماذا يحدث إذا لم يتطابق عدد المتغيرات؟

إذا كان val (a, b, c) = obj يحتوي على كائن بدالتي component فقط، سيُصدر المترجم خطأ. إذا كان هناك متغيرات أقل (val (a) = pair)، سيستدعي Kotlin فقط component1() ويتجاهل component2() — لن يحدث أي خطأ.

هل يمكن تفكيك Map بدون حلقة؟

نعم، Map.Entry يدعم التفكيك: val (key, value) = map.entries.first(). التفكيك لا يعمل على Map بأكمله مرة واحدة — فقط عنصرًا بعنصر، في حلقة for أو عبر مكرر.

هل يعمل التفكيك في اللامبدات؟

نعم، Kotlin يدعم التفكيك في معاملات اللامبدا: map.forEach { (k, v) -> ... }. لـ data classes في معاملات اللامبدا، تحتاج إلى إضافة أقواس متداخلة حول المعامل.

ما هو الحجم الأقصى للتفكيك؟

Kotlin يدعم حتى 5 متغيرات في إعلان التفكيك. إذا كنت بحاجة إلى تحليل أكثر من خمس خصائص، اجمعها في data classes متداخلة أو استخدم Pair/Triple داخل متغير واحد.

الخلاصة

  • Destructuring declaration — بناء val (a, b) = obj، يحلل كائنًا إلى عدة متغيرات في تعبير واحد
  • ComponentN() — دوال component1() و component2() و ... تُستدعى تلقائيًا بواسطة المترجم أثناء التفكيك
  • Data class تُنشئ تلقائيًا componentN() لجميع خصائص المنشئ الأساسي
  • Pair و Triple — فئات قياسية مع تفكيك مدمج لمكونين وثلاثة مكونات
  • الشرطة السفلية _ تستبدل المكونات غير المستخدمة دون إنشاء متغير
  • Operator componentN() في دوال الامتداد يُضيف التفكيك لأي فئة، بما في ذلك Java
  • Map.Entry في حلقة for ((k, v) in map) — النمط الاصطلاحي القياسي في Kotlin لتكرار قاموس

سنقوم بتطوير تطبيق جوال جاهز

تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.

مناقشة المشروع

اقرأ أيضًا