مقدمة: المشكلة التي تحلها Generics
تخيل أنك تكتب دالة لفرز قائمة من الأرقام. تكتبها من أجل Integer، وبعد أسبوع تحتاج إلى فرز قائمة من String. يضطرر إلى إنشاء دالة جديدة أو استخدام Object والقيام باستمرار بتحويل الأنواع. هذا غير مريح، وغير آمن، ويؤدي إلى أخطاء أثناء وقت التشغيل.
هذه هي المشكلة التي تحلها Generics (الجينيريكس) في Java. فهي تسمح بإنشاء كلاسات وواجهات ودوال تعمل مع أنواع بيانات مختلفة، مع الحفاظ على الكتابة الصارمة للأنواع في مرحلة التجميع. بدون الجينيريكس، ستكون Java الحديثة أقل راحة وأمانًا بكثير.
في هذه المقالة، سنشرح لماذا نحتاج إلى Generics، وكيف تعمل، والأهم من ذلك، كيفية تطبيقها في أمثلة واقعية.
1. أساسيات Generics: ما هي وكيف تعمل
1.1. مشكلة الأنواع "الخام" (raw types)
قبل ظهور Java 5 (عام 2004)، كان المطورون يستخدمون المجموعات دون تحديد النوع:
List list = new ArrayList();list.add("Hello");list.add(123);// عند الاسترجاع، يجب تذكر النوع الموجودString text = (String) list.get(0); // صحيحInteger num = (Integer) list.get(1); // صحيح// ولكن إذا أخطأت — ClassCastException في runtimeString error = (String) list.get(1); // خطأ!هذا الكود خطير: يتم اكتشاف الخطأ فقط عند تشغيل البرنامج.
1.2. كيف تعمل Generics
تسمح Generics بتحديد نوع البيانات الذي سيعمل به الكلاس أو الدالة، مباشرة بين قوسين زاويتين <T>. يتحقق المترجم من الأنواع في مرحلة التجميع ويقوم تلقائيًا بإدراج التحويلات الصحيحة.
الصيغة الأساسية:
// تعريف كلاس عامpublic class Box<T> { private T content; public void setContent(T content) { this.content = content; } public T getContent() { return content; }}
// الاستخدامBox<String> stringBox = new Box<>(); // <> — عامل الماس (Java 7+)stringBox.setContent("مرحبًا");String text = stringBox.getContent(); // بدون تحويل!
Box<Integer> intBox = new Box<>();intBox.setContent(42);int number = intBox.getContent(); // بدون تحويل!
// stringBox.setContent(123); // خطأ في التجميع!1.3. المزايا عمليًا
- أمان الأنواع: يتم اكتشاف الأخطاء في مرحلة التجميع، وليس في runtime.
- إزالة التحويلات الصريحة: يصبح الكود أنظف وأكثر قابلية للقراءة.
- إعادة استخدام الكود: كلاس/دالة واحدة تعمل مع أي نوع.
2. Generics عمليًا: الكلاسات والدوال والواجهات
2.1. الكلاسات العامة
الحالة الأكثر شيوعًا هي المجموعات. ولكن يمكنك إنشاء كلاسات عامة خاصة بك:
public class Pair<K, V> { private K key; private V value; public Pair(K key, V value) { this.key = key; this.value = value; } public K getKey() { return key; } public V getValue() { return value; }}
// الاستخدامPair<String, Integer> pair = new Pair<>("العمر", 30);String key = pair.getKey();Integer value = pair.getValue();2.2. الدوال العامة
يمكن أن تحتوي الدالة على معاملات نوع خاصة بها، غير مرتبطة بالكلاس:
public class Utils { // دالة عامة لإنشاء قائمة من مصفوفة public static <T> List<T> arrayToList(T[] array) { List<T> list = new ArrayList<>(); for (T element : array) { list.add(element); } return list; }}
// الاستخدامString[] names = {"آنا", "بوريس", "فيكتور"};List<String> nameList = Utils.arrayToList(names);