✅ أولًا: ما هو الـ 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,
};DartMap<K, V>هي Generic class:K= المفتاح (key)V= القيمة (value)
🧠 تسمية أنواع Generics:
| الاختصار | المعنى الشائع |
|---|---|
T | Type (نوع عام) |
E | Element (في List) |
K | Key (في Map) |
V | Value (في 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];
}
}Dartvoid 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
| المقارنة | dynamic | Generic |
|---|---|---|
| التحقق من النوع | وقت التشغيل | وقت الترجمة ✅ |
| مرن جدًا | ✅ | ✅ |
| آمن | ❌ ممكن خطأ | ✅ آمن جدًا |
| الأداء | أبطأ | أسرع غالبًا |
✅ خلاصة شاملة:
| عنصر | تفاصيل |
|---|---|
Generic | نوع عام يتم تحديده وقت الاستخدام |
T, K, V, E | رموز للأنواع |
| يمكن استخدامه في | كلاس – دوال – ميثود – كائنات |
| فائدة كبرى | إعادة استخدام الكود + أمان + مرونة |
| بديل عن | dynamic ولكن أكثر أمانًا |
| التحقق من النوع | يتم في وقت الترجمة (Compile Time) |