Generics في Java: لماذا هي ضرورية وكيفية استخدامها للمبتدئين
عند دراسة Java، عاجلاً أم آجلاً ستواجه مفهوم Generics (الأدوية الجنيسة، التعميمات). قد يبدو هذا معقدًا للمبتدئ، لكنه في الواقع أحد أكثر أدوات اللغة فائدة. في هذه المقالة، سنشرح ما هي Generics، ولماذا هي ضرورية وكيفية استخدامها بشكل صحيح. المادة موجهة للمطورين المبتدئين.
ما هي Generics ولماذا هي سيئة بدونها؟
Generics (التعميمات) هي آلية في Java تسمح بجعل الأنواع معلمة. بكلمات بسيطة، يمكنك كتابة كلاس أو واجهة أو طريقة تعمل مع أنواع بيانات مختلفة، مع الحفاظ على كتابة صارمة للأنواع في مرحلة التجميع.
تخيل أنك تريد إنشاء صندوق يمكنك وضع أي شيء فيه. بدون Generics، سيتعين عليك استخدام Object:
public class Box { private Object item;
public void put(Object item) { this.item = item; }
public Object get() { return item; }}استخدام مثل هذا الكلاس هو عذاب حقيقي:
Box box = new Box();box.put("Hello");// عند الاستخراج، يجب تحويل النوع بشكل صريحString text = (String) box.get(); // خطر ClassCastException!إذا قمت بوضع رقم عن طريق الخطأ، ثم حاولت تحويله إلى سلسلة نصية — سيتعطل البرنامج مع خطأ في وقت التشغيل. تحل Generics هذه المشكلة عن طريق نقل فحص الأنواع إلى مرحلة التجميع.
كيفية استخدام Generics: بناء الجملة الأساسي
دعنا نعيد كتابة كلاس Box الخاص بنا باستخدام Generics:
public class Box<T> { private T item;
public void put(T item) { this.item = item; }
public T get() { return item; }}هنا <T> هو معامل النوع. T هو اسم شائع (من كلمة "Type")، ولكن يمكنك استخدام أي حرف أو كلمة: E (Element)، K (Key)، V (Value) إلخ.
الآن أصبح الاستخدام آمنًا وأنيقًا:
Box<String> stringBox = new Box<>();stringBox.put("Hello");String text = stringBox.get(); // لا حاجة للتحويل!
// هذا السطر لن يتم تجميعه:// stringBox.put(123); // خطأ في التجميع!يعرف المترجم نفسه أن stringBox يعمل فقط مع السلاسل النصية. إذا حاولت وضع رقم، فلن يتم تجميع الكود حتى — ستعرف بالخطأ فورًا، وليس في وقت التشغيل.
Generics في المجموعات: أين هي الأكثر حاجة
أكثر مكان شائع لاستخدام Generics هو مجموعات Java (List, Set, Map وغيرها). قبل ظهور الأدوية الجنيسة (في Java 5)، كان المطورون يعانون مع ArrayList الذي كان يخزن Object:
// الطريقة القديمة (بدون Generics)List list = new ArrayList();list.add("مرحباً");list.add(123); // هذا يمكن إضافته أيضًا!
String item = (String) list.get(0); // طبيعيString item2 = (String) list.get(1); // ClassCastException!مع Generics كل شيء أبسط وأكثر أمانًا:
List<String> names = new ArrayList<>();names.add("Alice");names.add("Bob");// names.add(123); // خطأ في التجميع
for (String name : names) { System.out.println(name.toUpperCase()); // لا حاجة لأي تحويلات!}نفس الشيء يعمل مع Map:
Map<String, Integer> ageMap = new HashMap<>();ageMap.put("Alice", 30);ageMap.put("Bob", 25);
int aliceAge = ageMap.get("Alice"); // فتح تلقائي للـ intالقيود و wildcards: كيفية جعل Generics مرنة
أحيانًا تحتاج إلى تقييد الأنواع التي يمكن أن يقبلها الدواء الجنيس. لهذا تستخدم bounded type parameters (معاملات النوع المقيدة).
على سبيل المث