Generics في Kotlin: لماذا هي ضرورية وكيفية استخدامها عملياً

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

Generics في Kotlin: لماذا هي ضرورية وكيفية استخدامها عملياً



Generics (التعميمات) هي إحدى الميزات الرئيسية للغة Kotlin، والتي تتيح كتابة كود أكثر مرونة وأماناً من حيث الأنواع وقابلية لإعادة الاستخدام. إذا سبق لك كتابة List<String> أو Map<Int, User>، فقد تعاملت بالفعل مع الجينيريكس. ولكن لماذا هي ضرورية حقاً وكيف تستفيد منها إلى أقصى حد؟ في هذه المقالة، سنشرح النظرية ثم ننتقل إلى التطبيق العملي مع أمثلة حقيقية.



المشكلة التي تحلها Generics



تخيل أنك بحاجة لكتابة دالة تُرجع العنصر الأول من قائمة. بدون الجينيريكس، سيتعين عليك كتابة دالة منفصلة لكل نوع بيانات:



fun getFirstString(list: List<String>): String? = list.firstOrNull()fun getFirstInt(list: List<Int>): Int? = list.firstOrNull()fun getFirstUser(list: List<User>): User? = list.firstOrNull()


هذا يؤدي إلى تكرار الكود. استخدام Any سيحل المشكلة، لكنك ستفقد معلومات النوع في مرحلة الترجمة:



fun getFirst(list: List<Any>): Any? = list.firstOrNull()

// سيتعين عليك إجراء تحويل النوعval name: String? = getFirst(strings) as? String


تحل Generics هذه المشكلة بأناقة:



fun <T> getFirst(list: List<T>): T? = list.firstOrNull()

// المترجم يعرف النوع!val name: String? = getFirst(listOf("Alice", "Bob"))val age: Int? = getFirst(listOf(25, 30))


الآن دالة واحدة تعمل مع أي نوع، مع الحفاظ على أمان كامل للأنواع.



أساسيات Generics في Kotlin: الصياغة والإعلان



يتم إعلان التعميمات باستخدام الأقواس الزاوية <T>. الحرف T هو مجرد اتفاقية، يمكنك استخدام أي أسماء: E (عنصر)، K (مفتاح)، V (قيمة).



الفئات العامة (Generic Classes)



class Box<T>(val value: T) {    fun getContent(): T = value}

fun main() { val stringBox = Box("Hello") val intBox = Box(42) println(stringBox.getContent()) // Hello println(intBox.getContent()) // 42}


الدوال العامة (Generic Functions)



fun <T> printItem(item: T) {    println("Item: $item")}

// يمكن تحديد النوع بشكل صريحprintItem<String>("Kotlin")// أو الاعتماد على استنتاج الأنواعprintItem(100)


الواجهات العامة (Generic Interfaces)



interface Repository<T> {    fun getById(id: Int): T    fun save(item: T): Boolean}

class UserRepository : Repository<User> { override fun getById(id: Int): User { /* ... */ } override fun save(item: User): Boolean { /* ... */ }}


التباين: in, out والنجمة *



واحدة من أقوى ميزات Kotlin هي التباين (variance). تجيب على السؤال: هل يمكن أن يكون List<String> نوعاً فرعياً من List<Any>؟



التباين المشترك (Covariance - out)



استخدم out عندما يكون النوع العام الخاص بك يُرجع فقط (منتج). على سبيل المثال، List<T> متباين بشكل مشترك:



// يمكنك تعيين List<String> لمتغير من نوع List<Any>val strings: List<String> = listOf("a", "b")val anyList: List<Any> = strings // صحيح!

// لكن لا يمكنك إضافة عناصر// anyList.add(1) // خطأ في الترجمة!


مثال خاص بك:



class Producer<out T>(private val value: T) {    fun produce(): T = value}

fun main() { val stringProducer: Producer<String> = Producer("Hello") val anyProducer: Producer<Any> = stringProducer // يعمل! println(anyProducer.produce())}


التباين المعاكس (Contravariance - in)



استخدم in عندما يتم استقبال النوع فقط (مستهلك). على سبيل المثال، Comparable<T>:





                
                

المدونات

توصيات الكتب