مقدمة عن Generics في Kotlin
\n\nعند التطوير بلغة Kotlin، عاجلاً أم آجلاً ستواجه موقفاً حيث يجب أن تعمل نفس الخوارزمية أو بنية البيانات مع أنواع مختلفة من الكائنات. تخيل أنك تكتب دالة للبحث عن العنصر الأكبر في قائمة - يجب أن تعالج بشكل جيد على حد سواء قائمة الأرقام وقائمة السلاسل النصية. بدون أدوات خاصة، سيتعين عليك كتابة تطبيقات منفصلة لكل نوع. وهنا تأتي Generics (الأدوية الجنيسة أو التعميمات) لمساعدتك.
\n\nGenerics في Kotlin هي آلية قوية تسمح بإنشاء فئات ودوال وواجهات تعمل مع أنواع مختلفة من البيانات، مع الحفاظ على كتابة صارمة للأنواع في مرحلة التجميع. في هذه المقالة، سنشرح لماذا نحتاج إلى Generics، ونتعلم كيفية استخدامها مع أمثلة عملية.
\n\nما هي Generics ولماذا نحتاجها؟
\n\nتخيل صندوقاً يمكنه تخزين أي شيء: كتاب، هاتف، أو تفاحة. في البرمجة، هذا "الصندوق" هو فئة أو دالة معممة. بدون Generics، سيتعين عليك استخدام النوع Any (مشابه لـ Object في Java) والقيام باستمرار بتحويل الأنواع، مما يؤدي إلى أخطاء.
المشكلة بدون Generics
\n\nfun <T> printList(list: List<T>) {\n for (item in list) {\n println(item)\n }\n}\n\n// بدون Generics كان يجب كتابتها هكذا:\nfun printListAny(list: List<Any>) {\n for (item in list) {\n // يجب التحقق من النوع وتحويله\n if (item is String) {\n println("سلسلة نصية: $item")\n } else if (item is Int) {\n println("رقم: $item")\n }\n }\n}\n\nمزايا استخدام Generics
\n\n- \n
- أمان الأنواع — يتحقق المترجم من صحة استخدام الأنواع في مرحلة التجميع. \n
- إعادة استخدام الكود — دالة أو فئة واحدة تعمل مع أي أنواع. \n
- التخلي عن التحويلات الصريحة — يصبح الكود أنظف وأقل عرضة للأخطاء. \n
- الأداء — لا يوجد حمل إضافي للتحقق من الأنواع أثناء التنفيذ. \n
التراكيب الأساسية لـ Generics في Kotlin
\n\n1. الدوال العامة (Generic Functions)
\n\nالدالة العامة هي دالة تقبل معامل نوع. يتم تعريفها باستخدام الأقواس الزاوية <T> قبل اسم الدالة.
fun <T> getFirstElement(list: List<T>): T? {\n return list.firstOrNull()\n}\n\nfun main() {\n val numbers = listOf(1, 2, 3, 4, 5)\n val firstNumber = getFirstElement(numbers)\n println("الرقم الأول: $firstNumber") // 1\n\n val words = listOf("قط", "كلب", "ببغاء")\n val firstWord = getFirstElement(words)\n println("الكلمة الأولى: $firstWord") // قط\n}\n\nلاحظ: النوع T يتم استنتاجه تلقائياً بواسطة المترجم. يمكنك تحديده صراحة، لكن هذا ليس ضرورياً:
val firstNumber = getFirstElement<Int>(numbers)\n\n2. الفئات العامة (Generic Classes)
\n\nالفئة العامة هي فئة يمكنها تخزين ومعالجة كائنات من أي نوع. مثال ممتاز هو الفئة القياسية Box.
class Box<T>(private val content: T) {\n fun getContent(): T {\n return content\n }\n\n fun isTypeOf(typeClass: Class<*>): Boolean {\n return typeClass.isInstance(content)\n }\n}\n\nfun main() {\n val stringBox = Box("مرحباً، أيها العالم!")\n println(stringBox.getContent()) // مرحباً، أيها العالم!\n\n val intBox = Box(42)\n println(intBox.getContent()) // 42\n\n // التحقق من النوع\n println(intBox.isTypeOf(Int::class.java)) // true\n}\n\n3. الواجهات العامة (Generic Interfaces)
\n\nيمكن أن تكون الواجهات معممة أيضاً. هذا مفيد بشكل خاص عند إنشاء مجموعات أو عقود لمعالجة البيانات.
\n\ninterface Repository<T> {\n