Generics في Java: كيف تتوقف عن الخوف من الأقواس الزاوية وتبدأ في كتابة كود موثوق
\n\nفي عالم Java، هناك شيء يخيف المبتدئين تقريبًا أكثر من تعدد الخيوط — وهو الأقواس الزاوية <>. Generics (المعروفة أيضًا باسم الجينيريكس أو التعميمات) تبدو غامضة، لكنها في الواقع حليفك الرئيسي في النضال من أجل كود نظيف وآمن. دعنا نفهم كيف يعمل التصنيف في Java من خلال أمثلة يمكنك تطبيقها اليوم.
تخيل: أنت تكتب طريقة يجب أن تعمل مع الأرقام، والسلاسل النصية، والفئات الخاصة بك. بدون Generics، سيتعين عليك إنشاء عشرات الطرق المحملة بشكل زائد أو استخدام Object والتحقق من الأنواع يدويًا باستمرار. التعميمات في Java تحل هذه المشكلة بأناقة وأمان.
لماذا يتحول الكود بدون Generics إلى حقل ألغام؟
\n\nدعنا نتذكر كيف كان المبرمجون يعيشون قبل ظهور Generics (قبل Java 5). إذا كنت قد كتبت يومًا ArrayList دون تحديد النوع، فأنت تعرف هذا الكابوس:
List list = new ArrayList();\nlist.add("مرحبًا");\nlist.add(42);\nlist.add(new Object());\n\n// في مكان ما بعد 100 سطر من الكود:\nString text = (String) list.get(1); // ClassCastException في وقت التشغيل!\n\nالمشكلة الكلاسيكية: ClassCastException — خطأ يظهر فقط أثناء التنفيذ. يمكنك تمرير القائمة إلى طريقة أخرى، وهناك شخص متأكد من أنها تحتوي فقط على سلاسل نصية. وفجأة! التطبيق ينهار. Generics في Java تنقل التحقق من الأنواع إلى مرحلة التجميع — إنها مثل التأمين الذي يعمل قبل أن تجلس خلف عجلة القيادة.
\n\nGenerics في Java: البنية الأساسية التي يجب أن تعرفها
\n\nفي جوهر Generics فكرة بسيطة: نقوم بتمييز الفئة أو الطريقة بنوع. بدلاً من نوع محدد، نكتب معامل النوع (عادةً حرف كبير واحد: T، E، K، V).
المثال الأكثر شيوعًا هو المجموعات في Java:
\n\n// بدون Generics — خطر\nList rawList = new ArrayList();\n\n// مع Generics — آمن\nList<String> safeList = new ArrayList<>();\nsafeList.add("Java");\nsafeList.add("Generics");\n// safeList.add(123); // خطأ في التجميع!\n\nالمترجم الآن يعرف: داخل safeList يمكن أن تكون هناك سلاسل نصية فقط. محاولة إضافة رقم ستسبب خطأ في مرحلة التجميع، وليس في وقت التشغيل. هذا هو أمان الأنواع في Java في العمل.
إنشاء أول فئة عامة خاصة بك
\n\nلنفترض أننا نكتب حاوية بسيطة لتخزين قيمة واحدة. بدون Generics، سيتعين علينا استخدام Object:
public class Box {\n private Object value;\n\n public void set(Object value) { this.value = value; }\n public Object get() { return value; }\n}\n\nوالآن — مع Generics:
\n\npublic class Box<T> {\n private T value;\n\n public void set(T value) { this.value = value; }\n public T get() { return value; }\n}\n\n// الاستخدام:\nBox<Integer> intBox = new Box<>();\nintBox.set(100);\nint value = intBox.get(); // بدون تحويل!\n\nلاحظ: لم نعد بحاجة لكتابة (Integer) intBox.get(). المترجم يعرف بنفسه أن get() سيعيد Integer. هذا هو سحر التعميمات في Java.
التصنيف في Java: القيود و wildcards
\n\nكانت Generics بسيطة جدًا لولا التفاصيل الدقيقة. أهمها — bounded type parameters (معاملات النوع المحدودة). في بعض الأحيان تحتاج إلى القول: "النوع T يجب أن يكون رقمًا" أو "T يجب أن ينفذ واجهة Comparable".
\n\n// T يمكن أن يكون فقط Number أو فئة فرعية منه\npublic class Calculator<T extends Number> {\n private T value;\n\n public double square() {\n return value.doub