Generics في Java: لماذا هي ضرورية وكيفية استخدامها للمبتدئين
\n\nإذا كنت قد بدأت للتو في تعلم Java، فستواجه عاجلاً أم آجلاً مفهوم Generics (الأنواع العامة أو الجينيريكس). هذه الميزة القوية في اللغة تتيح لك كتابة كود أكثر مرونة وأماناً وقابلية لإعادة الاستخدام. في هذه المقالة، سنشرح بكلمات بسيطة ما هي Generics، ولماذا هي ضرورية وكيفية تطبيقها عملياً.
\n\nما هي Generics في Java؟
\n\nGenerics (من الكلمة الإنجليزية "generic" — عام، جنسي) هي آلية في Java تسمح بإنشاء كلاسات وواجهات ودوال تعمل مع أنواع بيانات مختلفة، مع ضمان فحص صارم للأنواع في مرحلة الترجمة (Compilation).
\n\nببساطة، تسمح لك الجينيريكس بتحديد: "هذه الكلاس أو الدالة ستعمل مع أي نوع بيانات، لكنني لا أعرف بعد أي نوع بالضبط". يتم تحديد النوع لاحقاً، عند إنشاء كائن أو استدعاء دالة.
\n\nالمشكلة بدون Generics
\n\nقبل ظهور Generics (في Java 5)، كان المطورون يستخدمون تحويل الأنواع (Casting) ويعملون مع Object. كان هذا غير مريح وخطير:
// بدون Generics (الطريقة القديمة)\nList list = new ArrayList();\nlist.add("مرحباً");\nlist.add(123); // خطأ؟ لا، المترجم سيمرره\n\nString text = (String) list.get(0); // التحويل إجباري\n// String num = (String) list.get(1); // ClassCastException أثناء التنفيذ!\n\nهنا لا يرى المترجم الخطأ، لكن البرنامج سيتعطل أثناء التنفيذ. Generics تحل هذه المشكلة.
\n\nلماذا نحتاج Generics في Java؟
\n\nتقدم Generics ثلاث مزايا رئيسية:
\n\n- \n
- أمان الأنواع (Type Safety) — يتم اكتشاف الأخطاء في مرحلة الترجمة، وليس أثناء تنفيذ البرنامج. \n
- الاستغناء عن التحويل الصريح (No Casting) — لا حاجة لكتابة
(String)في كل مرة. \n - إعادة استخدام الكود (Code Reuse) — كلاس أو دالة واحدة يمكن أن تعمل مع أنواع مختلفة. \n
مثال مع Generics
\n\nشاهد كيف يتغير الكود باستخدام الجينيريكس:
\n\n// مع Generics\nList<String> list = new ArrayList<>();\nlist.add("مرحباً");\n// list.add(123); // خطأ في الترجمة! لا يمكن إضافة Integer\n\nString text = list.get(0); // لا حاجة للتحويل\n// المترجم يعرف بالفعل أنها String\n\nأصبح الكود أبسط وأكثر أماناً وأسهل للقراءة.
\n\nكيفية استخدام Generics: الهياكل الأساسية
\n\nدعنا نستعرض ثلاث طرق رئيسية لتطبيق الجينيريكس: في الكلاسات، والدوال، والواجهات.
\n\n1. كلاسات Generic
\n\nيمكنك إنشاء كلاس يعمل مع أي نوع. للقيام بذلك، بعد اسم الكلاس بين قوسين زاوية، يتم تحديد معامل النوع (عادةً T، E، K، V):
// كلاس عام "صندوق"\npublic class Box<T> {\n private T content;\n\n public void put(T item) {\n this.content = item;\n }\n\n public T get() {\n return content;\n }\n}\n\n// الاستخدام\nBox<String> stringBox = new Box<>();\nstringBox.put("كتاب");\nString book = stringBox.get(); // بدون تحويل\n\nBox<Integer> intBox = new Box<>();\nintBox.put(42);\nint number = intBox.get();\n\n2. دوال Generic
\n\nيمكن جعل دالة واحدة فقط عامة، وليس الكلاس بأكمله. هذا مفيد عندما تحتاج الدالة إلى معالجة أنواع مختلفة بغض النظر عن الكلاس:
\n\npublic class Utils {\n // دالة عامة\n public static <T> void printArray(T[] array) {\n for (T element : array) {\n System.out.print(element + " ");\n }\n System.out.println();\n }\n}\n\n// الاستدعاء\nInteger[] intArray = {1, 2, 3};\nString[] strArray = {"A", "B", "C"};\n\nUtils.printArray(intArray); // 1 2 3\nUtils.printArray(strArray); // A B C\n\n3. واجهات Generic
\n\nالواجهة