English 🌐
11 الدرس 4 من 5

الواجهات المعممة (Generic Interfaces) — Comparable<T>

تعرّف على كيفية استخدام الواجهات (Interfaces) للأنماط المعممة (Generics) لفرض عقود آمنة الأنواع (Type-Safe Contracts)

Comparable<T> أمان الأنواع (Type Safety) String مقابل Date عقد الواجهة (Interface Contract)

1 الواجهات يمكن أن تكون معممة أيضًا

لقد رأينا الأصناف المعممة (Generic Classes) في الدرس 2 والتوابع المعممة (Generic Methods) في الدرس 3. والآن دعنا نرى أن الواجهات (Interfaces) يمكن أن تكون معممة أيضًا. في الواقع، لقد استخدمت واجهة معممة من قبل دون أن تدرك ذلك!

تمامًا مثل الأصناف (Classes)، يمكن للواجهات (Interfaces) أن تحتوي على معاملات أنواع (Type Parameters). الصياغة هي نفسها تمامًا — تضع معامل النوع بين أقواس زاوية مباشرة بعد اسم الواجهة:

Generic Interface Syntax
public interface InterfaceName<T> { T doSomething(T input); void process(T item); }
الصياغة الأساسية (Key Syntax)
النمط interface Name<T> يعلن عن واجهة معممة (Generic Interface). يمكن بعد ذلك استخدام معامل النوع T في جميع أنحاء الواجهة كعنصر نائب لأي نوع. عندما يقوم صنف بتنفيذ هذه الواجهة، فإنه يحدد النوع الفعلي.

2 هل تذكر Comparable؟ (بدون الأنماط المعممة)

هل تتذكر واجهة Comparable من الوحدة 9 (الواجهات - Interfaces)؟ قبل Java 5، كانت تبدو هكذا:

Comparable.java — Before Java 5 (no generics)
// Before Java 5 — no generics public interface Comparable { int compareTo(Object o); }

التابع compareTo يأخذ Object كمعامل. وهذا يعني أنه يمكنك مقارنة أي شيء مع أي شيء — مقارنة String مع Date، أو Integer مع Circle. المترجم (Compiler) لا يمكنه منعك.

المشكلة (Problem) مقارنة التفاح بالبرتقال

The Dangerous Code
String name = "Ali"; Date today = new Date(); // Both String and Date implement Comparable // So this compiles fine: int result = name.compareTo(today); // Comparing String with Date?!
كارثة وقت التشغيل! (Runtime Disaster!)
هذا الكود يُترجم بدون أي خطأ! المترجم (Compiler) يرى Object ويقول "حسنًا، لا مشكلة." لكن أثناء وقت التشغيل (Runtime)، مقارنة String مع Date لا معنى لها — وسيؤدي ذلك إلى رمي ClassCastException! البرنامج ينهار، ولن تكتشف الخطأ إلا عندما يبلغ عنه أحد المستخدمين.

3 Comparable مع الأنماط المعممة

بدءًا من Java 5، أُعيد تصميم واجهة Comparable باستخدام الأنماط المعممة (Generics). دعنا نرى النسخة الجديدة:

Comparable.java — Java 5+ (with generics)
// Java 5+ — with generics public interface Comparable<T> { int compareTo(T o); }

الآن T هو معامل النوع (Type Parameter). عندما يقوم صنف بتنفيذ Comparable<String>، فإن تابع compareTo يقبل فقط String. لا مزيد من Object — المترجم (Compiler) يعرف النوع الدقيق!

مثال (Example) كيف ينفّذ String واجهة Comparable<String>

String.java (simplified)
// String implements Comparable<String> public final class String implements Comparable<String> { @Override public int compareTo(String o) { // Compare this string with another String only } }

لاحظ أن compareTo الآن يأخذ String — وليس Object. تم استبدال معامل النوع المعمم T بالنوع المحدد String.

استخدام آمن (Safe Usage) المترجم يحميك

Type-Safe Comparison
String name1 = "Ali"; String name2 = "Sara"; int result = name1.compareTo(name2); // String vs String — OK! Date today = new Date(); // name1.compareTo(today); // COMPILE ERROR! // Error: incompatible types: Date cannot be converted to String
تم اكتشاف الخطأ وقت الترجمة! (Error Caught at Compile Time!)
تم اكتشاف الخطأ في وقت الترجمة (Compile Time)! المدقق الإملائي للأنماط المعممة (Generics) يمنعنا من مقارنة String مع Date. المترجم (Compiler) يقول: "لا، لقد أعلنت عن Comparable<String>، لذا يمكنك المقارنة فقط مع String."

4 مقارنة جنبًا إلى جنب

دعنا نضع النهج القديم والجديد جنبًا إلى جنب حتى تتمكن من رؤية الفرق بوضوح:

قبل — بدون أنماط معممة
Comparable c = "Ali"; // Compiles fine... c.compareTo(new Date()); // ...but CRASHES at runtime! // ClassCastException
بعد — مع الأنماط المعممة
Comparable<String> c = "Ali"; // COMPILE ERROR! c.compareTo(new Date()); // Error caught BEFORE running // Safe and sound
النمط (The Pattern)
بدون الأنماط المعممة (Generics): يختبئ الخطأ حتى وقت التشغيل (Runtime). مع الأنماط المعممة: يكتشفه المترجم (Compiler) فورًا. هذا هو نفس الموضوع الذي رأيناه طوال هذه الوحدة — الأنماط المعممة تنقل الأخطاء من وقت التشغيل (Runtime) إلى وقت الترجمة (Compile Time).

5 إنشاء واجهتك المعممة الخاصة

أنت لست مقيدًا باستخدام واجهات Java المعممة المدمجة. يمكنك إنشاء واجهاتك الخاصة! دعنا نبني واجهة Container<T> بسيطة تحدد عقدًا لتخزين واسترجاع العناصر من أي نوع.

الخطوة 1 (Step 1) تعريف الواجهة المعممة (Define the Generic Interface)

Container.java
public interface Container<T> { void add(T item); T get(int index); int size(); }

الخطوة 2 (Step 2) تنفيذ الواجهة (Implement the Interface)

SimpleList.java
public class SimpleList<T> implements Container<T> { private Object[] items = new Object[100]; private int count = 0; @Override public void add(T item) { items[count++] = item; } @Override @SuppressWarnings("unchecked") public T get(int index) { return (T) items[index]; } @Override public int size() { return count; } }

لاحظ أن SimpleList<T> يمرر معامل النوع الخاص به T إلى Container<T>. هذا يعني أن الصنف المنفِّذ يبقى معممًا — المستخدم يقرر النوع عند إنشاء كائن SimpleList.

الخطوة 3 (Step 3) استخدامها مع أمان الأنواع (Use It with Type Safety)

Main.java
SimpleList<String> names = new SimpleList<>(); names.add("Ali"); names.add("Sara"); String first = names.get(0); // No casting needed! // names.add(42); // COMPILE ERROR! Only String allowed
خلف الكواليس (Behind the Scenes)
هذا هو بالضبط كيف يعمل ArrayList<E> في Java داخليًا! صنف ArrayList ينفّذ واجهة List<E>، وهي واجهة معممة (Generic Interface). الآن أنت تفهم القوة الكامنة وراءها.
طريقتان لتنفيذ واجهة معممة (Two Ways to Implement a 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)
الدرس 5: محو الأنواع (Type Erasure) ← → الدرس 3: التوابع المعممة (Generic Methods)
سمة كتل الكود