Generic In dart

✅ أولًا: ما هو الـ Generic؟
📘 التعريف:

Generic هو مفهوم يسمح لك بكتابة كود مرن وقابل لإعادة الاستخدام، بدون تحديد نوع البيانات مسبقًا.

ببساطة:
بدل ما تكتب كود يتعامل مع int أو String فقط…
تخليه يقبل أي نوع يجي وقت التنفيذ (runtime).

🔍 الفكرة العامة:
class Box<T> {
  T value;

  Box(this.value);

  T getValue() => value;
}
Dart

هنا:

  • <T> معناها “نوع عام” (Type Parameter).
  • يمكننا استخدام أي نوع مكان T: مثل Box<int> أو Box<String> أو Box<User>.
🔧 لماذا نستخدم Generics؟
الفائدةالتوضيح
✅ إعادة استخدام الكودبدل ما تكتب نفس الكود لكل نوع
✅ تقليل الأخطاء في وقت التشغيلالتحقق من النوع يتم في وقت الترجمة (compile)
✅ كتابة مكتبات وأدوات عامةمثل List<T>, Map<K, V>
✅ تحسين الأداء والوضوحبدال dynamic الغامض
🧪 مثال 1: كلاس Box بسيط
class Box<T> {
  T value;

  Box(this.value);

  void showValue() {
    print("القيمة: $value");
  }
}

void main() {
  Box<int> intBox = Box(5);
  intBox.showValue(); // القيمة: 5

  Box<String> stringBox = Box("Hello");
  stringBox.showValue(); // القيمة: Hello
}
Dart
✅ شرح المثال:
  • أنشأنا كلاس عام اسمه Box يقبل نوع T.
  • استخدمناه مع int مرة، ومع String مرة.
  • هذا هو معنى Generic: كود واحد → عدة أنواع.
🎯 Generic في الدوال
T getFirst<T>(List<T> items) {
  return items[0];
}

void main() {
  print(getFirst<int>([1, 2, 3]));        // 1
  print(getFirst<String>(["a", "b"]));    // a
}
Dart

دالة تأخذ لستة وتعيد أول عنصر، مهما كان نوعها.

🧱 Generic في الميثود داخل كلاس
class Logger {
  void log<T>(T value) {
    print("LOG: $value");
  }
}

void main() {
  Logger logger = Logger();
  logger.log<String>("رسالة");
  logger.log<int>(123);
}
Dart

هنا الدالة فقط Generic، بينما الكلاس نفسه ليس.

📚 Generic في الـ Map:
Map<String, int> ages = {
  "Ali": 20,
  "Sara": 25,
};
Dart
  • Map<K, V> هي Generic class:
    • K = المفتاح (key)
    • V = القيمة (value)
🧠 تسمية أنواع Generics:
الاختصارالمعنى الشائع
TType (نوع عام)
EElement (في List)
KKey (في Map)
VValue (في Map)
S, Uأنواع أخرى إضافية
🧪 مثال عملي 2: كلاس قائمة عامة (Generic List)
class MyList<T> {
  List<T> items = [];

  void add(T item) {
    items.add(item);
  }

  T getItem(int index) {
    return items[index];
  }
}
Dart
void main() {
  MyList<String> names = MyList();
  names.add("Yasin");
  names.add("Khalid");
  print(names.getItem(0)); // Yasin

  MyList<int> numbers = MyList();
  numbers.add(100);
  print(numbers.getItem(0)); // 100
}
Dart
🔒 تقييد النوع (Type Constraint)

تقدر تقول: هذا الـ T لازم يكون نوع معين أو يرث منه:

class Animal {
  void move() => print("الحيوان يتحرك");
}

class Dog extends Animal {}

class Box<T extends Animal> {
  T animal;

  Box(this.animal);

  void makeMove() {
    animal.move(); // ✅ مضمون إنه يملك move()
  }
}
Dart

لو حاولت تعمل Box<int> → ❌ خطأ.

🔁 استخدام multiple types:
class Pair<A, B> {
  A first;
  B second;

  Pair(this.first, this.second);
}

void main() {
  Pair<String, int> user = Pair("Ali", 25);
  print("${user.first}, ${user.second}"); // Ali, 25
}
Dart
📌 الفرق بين dynamic و Generic
المقارنةdynamicGeneric
التحقق من النوعوقت التشغيلوقت الترجمة ✅
مرن جدًا
آمن❌ ممكن خطأ✅ آمن جدًا
الأداءأبطأأسرع غالبًا
✅ خلاصة شاملة:
عنصرتفاصيل
Genericنوع عام يتم تحديده وقت الاستخدام
T, K, V, Eرموز للأنواع
يمكن استخدامه فيكلاس – دوال – ميثود – كائنات
فائدة كبرىإعادة استخدام الكود + أمان + مرونة
بديل عنdynamic ولكن أكثر أمانًا
التحقق من النوعيتم في وقت الترجمة (Compile Time)