Menu
Coddy logo textTech
flag Ar iconالعربيةdown icon

مقدمة في الأنواع العامة

جزء من قسم Object Oriented Programming في رحلة Java على Coddy — الدرس 53 من 87.

تخيل أنك بحاجة إلى إنشاء فئة (class) تخزن قيمة وتسترجعها لاحقاً. بدون الـ generics، سيتعين عليك كتابة فئات منفصلة لكل نوع—واحدة لـ Integer، وواحدة لـ String، وواحدة لـ Double، وهكذا. يؤدي هذا إلى تكرار الكود وصعوبات في الصيانة.

تعالج الأنواع العامة (Generics) هذه المشكلة من خلال السماح لك بكتابة فئات (classes) وطرق (methods) تعمل مع أي نوع، مع الحفاظ في الوقت نفسه على سلامة النوع (type safety). تقوم بتعريف عنصر نائب (يسمى معامل النوع - type parameter) يتم استبداله بنوع فعلي عند استخدام الفئة:

// بدون الأنواع العامة - يستخدم Object، يتطلب التحويل (casting)
List list = new ArrayList();
list.add("Hello");
String s = (String) list.get(0);  // التحويل اليدوي مطلوب

// مع الأنواع العامة - آمن من حيث النوع، لا يوجد تحويل
List<String> list = new ArrayList<String>();
list.add("Hello");
String s = list.get(0);  // لا حاجة للتحويل

تشير الأقواس الزاوية <T> إلى معلمة نوع. عندما تكتب List<String>، فإنك تخبر المترجم أن هذه القائمة ستحتوي فقط على سلاسل نصية. إذا حاولت إضافة عدد صحيح، فسيقوم المترجم باكتشاف الخطأ فوراً بدلاً من الفشل في وقت التشغيل.

توفر الأنواع العامة (Generics) فائدتين رئيسيتين: فهي تلغي الحاجة إلى التحويل الصريح للأنواع (explicit casting)، وتكتشف أخطاء النوع في وقت التجميع (compile time) بدلاً من وقت التشغيل (runtime). وهذا يجعل الكود الخاص بك أكثر أماناً ونظافة. في الدروس التالية، سنستكشف كيفية إنشاء الفئات (classes) والطرق (methods) العامة الخاصة بك.

challenge icon

التحدي

سهل

لنقم ببناء نظام تخزين بسيط يوضح قوة القوالب العامة (generics) من خلال مقارنة الطريقة القديمة (باستخدام Object مع التحويل القسري casting) مقابل الطريقة الجديدة (باستخدام generics مع سلامة النوع type safety).

ستقوم بإنشاء ثلاثة ملفات لترى الفرق بنفسك:

  • OldBox.java: أنشئ فئة (class) تخزن القيم بالطريقة التي كانت متبعة قبل ظهور generics. يجب أن تحتوي OldBox على حقل خاص content من نوع Object. قم بتضمين منشئ (constructor) يقبل Object ويقوم بتخزينه، وطريقة getContent() تعيد الـ Object. هذا النهج يعمل ولكنه يتطلب تحويلاً قسرياً (casting) عند استرجاع القيم.
  • GenericBox.java: الآن أنشئ النسخة الحديثة والآمنة من حيث النوع باستخدام generics. يجب أن تستخدم GenericBox معامل نوع T في إعلانها. تحتاج إلى حقل خاص content من نوع T، ومنشئ يقبل قيمة من نوع T، وطريقة getContent() تعيد النوع T. لا حاجة للتحويل القسري عند استخدام هذه الفئة!
  • Main.java: استعرض كلا النهجين جنباً إلى جنب. ستتلقى مدخلين: كلمة (String) ورقم (integer).

    أولاً، استخدم النهج القديم: أنشئ OldBox يخزن الكلمة، ثم استرجعها مع تحويل قسري إلى String واطبع: OldBox (with cast): [value]

    بعد ذلك، استخدم نهج generics: أنشئ GenericBox<String> يخزن نفس الكلمة، واسترجعها بدون تحويل قسري، واطبع: GenericBox (no cast): [value]

    ثم أظهر عمل generics مع الأعداد الصحيحة: أنشئ GenericBox<Integer> يخزن الرقم، واسترجعه، واطبع: GenericBox Integer: [value]

    أخيراً، اطبع سطراً فارغاً يليه: Type safety: Generics catch errors at compile time!

ستتلقى مدخلين بالترتيب: كلمة (String) ورقم (integer كـ String، والذي ستقوم بتحليله).

لاحظ الفرق الجوهري: مع OldBox، يجب عليك إجراء تحويل قسري (String) box.getContent() وتأمل أنك اخترت النوع الصحيح. أما مع GenericBox، فإن المترجم (compiler) يعرف النوع ولا حاجة للتحويل القسري. هذه هي الفائدة الأساسية لـ generics — سلامة النوع بدون عناء التحويل القسري!

ورقة مرجعية

تسمح لك القوالب العامة (Generics) بكتابة فئات (classes) وطرق (methods) تعمل مع أي نوع مع الحفاظ على سلامة النوع (type safety). وهي تستخدم معاملات النوع (type parameters) (عناصر نائبة) يتم استبدالها بأنواع فعلية عند استخدام الفئة.

بدون القوالب العامة (Generics) (باستخدام Object):

List list = new ArrayList();
list.add("Hello");
String s = (String) list.get(0);  // Manual casting required

باستخدام القوالب العامة (Generics) (آمن النوع):

List<String> list = new ArrayList<String>();
list.add("Hello");
String s = list.get(0);  // No casting needed

إنشاء فئة عامة (generic class):

public class GenericBox<T> {
    private T content;
    
    public GenericBox(T content) {
        this.content = content;
    }
    
    public T getContent() {
        return content;
    }
}

استخدام فئة عامة:

GenericBox<String> stringBox = new GenericBox<String>("Hello");
String value = stringBox.getContent();  // No casting

GenericBox<Integer> intBox = new GenericBox<Integer>(42);
Integer number = intBox.getContent();  // Type-safe

تشير الأقواس الزاوية <T> إلى معامل النوع. تشمل الفوائد: عدم الحاجة إلى تحويل صريح (casting)، واكتشاف أخطاء النوع في وقت التجميع (compile time) بدلاً من وقت التشغيل (runtime).

جرّب بنفسك

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String word = scanner.nextLine();
        int number = Integer.parseInt(scanner.nextLine());
        
        // TODO: قم بإنشاء OldBox لتخزين الكلمة
        // استرجع القيمة مع تحويل (cast) إلى String واطبع: "OldBox (with cast): [value]"
        
        // TODO: قم بإنشاء GenericBox<String> لتخزين الكلمة
        // استرجع القيمة بدون تحويل (casting) واطبع: "GenericBox (no cast): [value]"
        
        // TODO: قم بإنشاء GenericBox<Integer> لتخزين الرقم
        // استرجع القيمة واطبع: "GenericBox Integer: [value]"
        
        // TODO: اطبع سطراً فارغاً، ثم اطبع:
        // "Type safety: Generics catch errors at compile time!"
    }
}
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس Object Oriented Programming