1 الواجهات يمكن أن تكون معممة أيضًا
لقد رأينا الأصناف المعممة (Generic Classes) في الدرس 2 والتوابع المعممة (Generic Methods) في الدرس 3. والآن دعنا نرى أن الواجهات (Interfaces) يمكن أن تكون معممة أيضًا. في الواقع، لقد استخدمت واجهة معممة من قبل دون أن تدرك ذلك!
تمامًا مثل الأصناف (Classes)، يمكن للواجهات (Interfaces) أن تحتوي على معاملات أنواع (Type Parameters). الصياغة هي نفسها تمامًا — تضع معامل النوع بين أقواس زاوية مباشرة بعد اسم الواجهة:
interface Name<T> يعلن عن واجهة معممة (Generic Interface). يمكن بعد ذلك استخدام معامل النوع T في جميع أنحاء الواجهة كعنصر نائب لأي نوع. عندما يقوم صنف بتنفيذ هذه الواجهة، فإنه يحدد النوع الفعلي.
2 هل تذكر Comparable؟ (بدون الأنماط المعممة)
هل تتذكر واجهة Comparable من الوحدة 9 (الواجهات - Interfaces)؟ قبل Java 5، كانت تبدو هكذا:
التابع compareTo يأخذ Object كمعامل. وهذا يعني أنه يمكنك مقارنة أي شيء مع أي شيء — مقارنة String مع Date، أو Integer مع Circle. المترجم (Compiler) لا يمكنه منعك.
المشكلة (Problem) مقارنة التفاح بالبرتقال
Object ويقول "حسنًا، لا مشكلة." لكن أثناء وقت التشغيل (Runtime)، مقارنة String مع Date لا معنى لها — وسيؤدي ذلك إلى رمي ClassCastException! البرنامج ينهار، ولن تكتشف الخطأ إلا عندما يبلغ عنه أحد المستخدمين.
3 Comparable مع الأنماط المعممة
بدءًا من Java 5، أُعيد تصميم واجهة Comparable باستخدام الأنماط المعممة (Generics). دعنا نرى النسخة الجديدة:
الآن T هو معامل النوع (Type Parameter). عندما يقوم صنف بتنفيذ Comparable<String>، فإن تابع compareTo يقبل فقط String. لا مزيد من Object — المترجم (Compiler) يعرف النوع الدقيق!
مثال (Example) كيف ينفّذ String واجهة Comparable<String>
لاحظ أن compareTo الآن يأخذ String — وليس Object. تم استبدال معامل النوع المعمم T بالنوع المحدد String.
استخدام آمن (Safe Usage) المترجم يحميك
String مع Date. المترجم (Compiler) يقول: "لا، لقد أعلنت عن Comparable<String>، لذا يمكنك المقارنة فقط مع String."
4 مقارنة جنبًا إلى جنب
دعنا نضع النهج القديم والجديد جنبًا إلى جنب حتى تتمكن من رؤية الفرق بوضوح:
5 إنشاء واجهتك المعممة الخاصة
أنت لست مقيدًا باستخدام واجهات Java المعممة المدمجة. يمكنك إنشاء واجهاتك الخاصة! دعنا نبني واجهة Container<T> بسيطة تحدد عقدًا لتخزين واسترجاع العناصر من أي نوع.
الخطوة 1 (Step 1) تعريف الواجهة المعممة (Define the Generic Interface)
الخطوة 2 (Step 2) تنفيذ الواجهة (Implement the Interface)
لاحظ أن SimpleList<T> يمرر معامل النوع الخاص به T إلى Container<T>. هذا يعني أن الصنف المنفِّذ يبقى معممًا — المستخدم يقرر النوع عند إنشاء كائن SimpleList.
الخطوة 3 (Step 3) استخدامها مع أمان الأنواع (Use It with Type Safety)
ArrayList<E> في Java داخليًا! صنف ArrayList ينفّذ واجهة List<E>، وهي واجهة معممة (Generic Interface). الآن أنت تفهم القوة الكامنة وراءها.
1. إبقاؤها معممة: class SimpleList<T> implements Container<T> — يبقى الصنف معممًا، والمستخدم يختار النوع لاحقًا.
2. تثبيت النوع: class StringList implements Container<String> — يقفل الصنف على نوع محدد. كل تابع يستخدم String مباشرة.
6 النقاط الرئيسية
-
1
الواجهات (Interfaces) يمكن أن تكون معممة، تمامًا مثل الأصناف (Classes):
interface Name<T> -
2
عند التنفيذ (Implementing)، حدد النوع:
implements Comparable<String> - 3 الواجهات المعممة (Generic Interfaces) تفرض عقودًا آمنة الأنواع (Type-Safe Contracts) — يتحقق المترجم (Compiler) من أن طرفي العقد متفقان على الأنواع
-
4
Comparable<T>يمنع مقارنة الأنواع غير المتوافقة — لا مزيد من أخطاء مقارنةStringمعDate -
5
واجهات Java المدمجة (
Comparable،Iterable،List، إلخ) جميعها تستخدم الأنماط المعممة (Generics) - 6 يمكنك إنشاء واجهاتك المعممة (Generic Interfaces) الخاصة لبناء تجريدات قابلة لإعادة الاستخدام وآمنة الأنواع (Type-Safe)