ما هو reified في Kotlin ولماذا هو "سحر"؟
\nإذا كنت قد كتبت بلغة Java من قبل، فبالتأكيد واجهت Type Erasure — محو الأنواع أثناء التنفيذ. في Java (وفي Kotlin افتراضيًا) تكون معلومات النوع داخل الأدوية الجنسية متاحة فقط في مرحلة التجميع، وتختفي في وقت التشغيل. هذا يعني أنه لا يمكنك كتابة if (value is T) أو T::class داخل دالة عادية، لأن T أثناء التنفيذ هو مجرد Object.
لكن Kotlin ذهب أبعد من ذلك. الكلمة المفتاحية reified (من الإنجليزية "مُجسَّد") تسمح لك "بإحياء" النوع داخل دالة inline. بفضل reified يمكنك فعل ما كان يبدو مستحيلًا في Java: التحقق من النوع، الحصول على KClass، إنشاء مثيلات، وحتى العمل مع الانعكاس — وكل ذلك في وقت التشغيل.
في هذه المقالة، سنشرح كيف يعمل reified بأمثلة حقيقية يمكنك تطبيقها فورًا. هيا بنا!
\n\nالمشكلة: Type Erasure في العمل
\nتخيل: أنت تكتب دالة يجب أن تتحقق مما إذا كان الكائن عبارة عن سلسلة نصية. محاولة ساذجة:
\nfun <T> isString(value: Any): Boolean {\n return value is T // خطأ في التجميع!\n}\nسيصدر المترجم خطأ: "Cannot check for instance of erased type: T". لماذا؟ لأنه أثناء التنفيذ، لا تعرف JVM ما هو T. بالنسبة لها، T هو مجرد Object.
نفس الشيء مع الحصول على الكلاس: T::class داخل دالة عادية لن يعمل أيضًا. هذا هو Type Erasure — الصداع الرئيسي لمطوري Java.
الحل: reified + inline
\nيقدم Kotlin مخرجًا أنيقًا. "لتذكر" النوع في وقت التشغيل، يجب جعل الدالة inline ووضع علامة على معامل النوع بالكلمة المفتاحية reified:
\ninline fun <reified T> isString(value: Any): Boolean {\n return value is T // الآن يعمل!\n}\nالآن يمكنك الاستدعاء:
\nprintln(isString<String>("Hello")) // true\nprintln(isString<Int>("Hello")) // false\nكيف يعمل هذا؟ عندما تضع علامة على دالة كـ inline، يقوم المترجم بتضمين كودها مباشرة في مكان الاستدعاء. و reified تجبر المترجم على استبدال النوع المحدد (مثل String) بدلاً من T في الكود المُنشأ. ونتيجة لذلك، يظهر في البايت كود تحقق صريح value is String.
أمثلة عملية لاستخدام reified
\nدعنا نلقي نظرة على بعض السيناريوهات الحقيقية حيث يبسط reified الحياة حقًا.
\n\n1. الحصول على الكلاس في وقت التشغيل
\nواحدة من أكثر المهام شيوعًا هي الحصول على KClass أو Class من معامل النوع. بدون reified هذا مستحيل:
inline fun <reified T> getKClass(): KClass<T> = T::class\n\n// الاستخدام:\nval kClass = getKClass<String>()\nprintln(kClass.simpleName) // String\nهذا مفيد بشكل خاص عند العمل مع مكتبات تتطلب الانعكاس (مثل التسلسل، ORM، حاويات DI).
\n\n2. إنشاء مثيل حسب النوع
\nهل تريد إنشاء كائن من النوع المطلوب دون تمرير المُنشئ؟ بسهولة:
\ninline fun <reified T : Any> createInstance(): T {\n return T::class.createInstance()\n}\n\n// مثال:\nval instance = createInstance<StringBuilder>()\ninstance.append("Hello, reified!")\nلكن كن حذرًا: createInstance() يعمل فقط إذا كان للكلاس مُنشئ بدون معاملات. وإلا ستحصل على استثناء.
3. التصفية حسب النوع في المجموعات
\nلنفترض أن لديك قائمة من كائنات من أنواع مختلفة، وتريد تصفية السلاسل النصية فقط. في المكتبة القياسية لـ Kotlin يوجد filterIsInstance<T>()، لكن يمكنك بسهولة كتابة نسختك الخاصة:
المدونات
توصيات الكتب
اقرأ أيضًا