لماذا الجينيريكس (المعمّمات)؟
لماذا الجينيريكس (المعمّمات)؟
أُضيفت الجينيريكس إلى Java 5 لحلّ مشكلة كانت تُعاني منها كل قاعدة كود تعتمد على المجموعات (Collections): الغياب التام لأمان النوع في وقت الترجمة. فهم سبب وجودها هو أسرع طريق لاستخدامها بشكل صحيح.
الحياة قبل الجينيريكس: عصر المجموعات الخام
قبل Java 5، كانت ArrayList (وكل مجموعة أخرى) تخزّن مراجع من نوع Object الخام. كنت تضع فيها أي شيء، وتحصل على Object عند الاسترجاع — مما يعني أنّك كنت مضطرًا لتحويل (cast) كل ما تسترجعه.
قبل الجينيريكس، كان المترجم يقبل هذا الكود دون أي تحذير. كان الخطأ يعيش بصمت حتى يواجهه المستخدم على شكل ClassCastException في وقت التشغيل.
أوجاع الثلاثة في عصر النوع الخام
- تحويلات النوع غير الآمنة في كل مكان. كل استدعاء
get()كان يحتاج إلى تحويل. انسَ واحدًا أو حوّل إلى نوع خاطئ، وستحصل على تعطّل في وقت التشغيل. - لا توثيق في النوع ذاته. معامل مكتوب كـ
Listلا يخبرك بشيء. هل هي قائمة منString؟ أمInteger؟ أم مختلطة؟ كنت مضطرًا لقراءة Javadoc أو الكود المصدري لتعرف. - لا مساعدة من الأدوات. لم تستطع بيئات التطوير اقتراح توابع على نوع العنصر لأنّ النوع كان ممحوًا إلى
Object. كان الإكمال التلقائي عديم الفائدة لمحتوى المجموعات.
الجينيريكس تحلّ المشاكل الثلاث
النوع المعمّم يحمل نوع عنصره كـ معامل نوع. تكتب ArrayList<String> بدلًا من ArrayList. يعرف المترجم الآن أن كل عنصر من نوع String ويُطبّق ذلك في وقت الترجمة.
ثلاثة مكاسب في تغيير واحد:
- رفض
add(42)الخاطئة في وقت الترجمة، لا في وقت التشغيل. - متغيّر الحلقة مكتوب كـ
Stringبالفعل — لا تحويل، لا ضجيج. - بيئة التطوير تقدّم الآن الإكمال التلقائي الكامل لـ
Stringداخل الحلقة.
الجينيريكس تتجاوز المجموعات
المجموعات هي حالة الاستخدام الأكثر وضوحًا، لكن الجينيريكس ميزة عامة في اللغة. أي فئة أو تابع يعمل على نوع لا يعرفه مسبقًا يمكن تعميمه. مثال كلاسيكي: Optional<T>.
Optional<String> تقول: "هذا الصندوق إمّا يحتوي على String أو فارغ." يُطبّق المترجم هذا الضمان. بدون الجينيريكس، كان Optional سيُعيد Object وستعود إلى تحويل الأنواع.
نظرة سريعة على نمط الصيغة
الصيغة بين الزوايا <T> تُعلن عن معامل نوع. عند استخدام النوع المعمّم، تُزوّد وسيطة نوع مثل String أو Integer:
نفس فئة Box تعمل مع أي نوع، ومع ذلك يكشف المترجم عدم التطابق في النوع لكل استخدام محدد. هذا هو الوعد الجوهري للجينيريكس: اكتب مرة واحدة، وتحقّق النوع لكل استخدام.
<> على الجانب الأيمن (كما في الأمثلة أعلاه) بدلًا من تكرار وسيطة النوع الكاملة. المترجم يستنتجها من الجانب الأيسر. تكرارها زائد ويُثقّل الكود.
الخلاصة
وُجدت الجينيريكس لأن المجموعات الخام كانت تُجبر المطوّرين على تحويل كل شيء، وتؤجّل أخطاء النوع إلى وقت التشغيل. بتعميم الأنواع بـ <T>، يستطيع المترجم التحقق من أن النوع الصحيح فقط يُخزَّن ويُسترجَع. النتيجة: كود أكثر أمانًا، وتوثيق أفضل من خلال الأنواع، وتجربة أفضل بكثير في بيئة التطوير — كل ذلك مجانًا في وقت التشغيل بسبب محو النوع (يُغطّى في الدرس 7). في الدروس التالية ستتعلم كتابة فئاتك وتوابعك المعمّمة الخاصة، وتقييد معاملات النوع بالحدود.