Null Safety في Dart: الأمان من القيم الفارغة
من أشهر أسباب انهيار التطبيقات في العالم خطأ بسيط: محاولة استخدام قيمة غير موجودة. مخترع هذه الفكرة نفسه سمّاها «خطأ المليار دولار» لكثرة ما سببته من مشاكل. Dart تحل هذه المشكلة بميزة اسمها Null Safety (الأمان من القيم الفارغة)، وهي من أهم ما يميزها. في هذا الدرس ستفهم الفكرة وتتعلم أدواتها.
ما هي null؟
Section titled “ما هي null؟”null تعني «لا قيمة». ليست صفراً، وليست نصاً فارغاً، بل غياب القيمة تماماً.
تخيّل خانة رقم الهاتف في استمارة: بعض الناس يكتبون رقمهم، وبعضهم يتركها فارغة. الخانة الفارغة ليست الرقم صفر، بل «لا يوجد رقم». هذه هي null.
المشكلة تظهر عندما تحاول استخدام قيمة غير موجودة: كأن تطلب من البرنامج «اتصل بالرقم المكتوب في الخانة»، والخانة فارغة! في لغات كثيرة، هذا يجعل التطبيق ينهار فجأة أمام المستخدم.
الحل: الأنواع لا تقبل null افتراضياً
Section titled “الحل: الأنواع لا تقبل null افتراضياً”في Dart، أي متغير عادي لا يمكن أن يكون null:
String name = 'سارة';name = null; // خطأ! المحرر يرفض هذا السطرهذا يعني أنك إذا رأيت متغيراً من نوع String، فأنت متأكد 100% أن فيه نصاً، ويمكنك استخدامه بأمان. المحرر يكشف المشكلة وأنت تكتب الكود، لا والمستخدم يستخدم التطبيق.
الأنواع التي تقبل null: علامة ?
Section titled “الأنواع التي تقبل null: علامة ?”إذا كانت القيمة قد تكون غير موجودة فعلاً، أضف ? بعد النوع:
String? nickname;print(nickname);nickname = 'سوسو';print(nickname);nullسوسوString?تعني: «نص، أو null».- المتغير الذي يقبل null وليست له قيمة، تكون قيمته
nullتلقائياً.
والآن، Dart لن تسمح لك باستخدام nickname مباشرة في أي شيء يحتاج نصاً مؤكداً، حتى تتعامل مع احتمال أنه فارغ. هذه هي الحماية.
العامل ??: قيمة احتياطية
Section titled “العامل ??: قيمة احتياطية”«إذا كانت القيمة null، فاستخدم هذه بدلاً منها»:
String? username;print('أهلاً يا ${username ?? 'زائر'}');أهلاً يا زائرومعه أخوه ??=: «أسند القيمة فقط إذا كان المتغير null الآن»:
int? stock;stock ??= 10;print(stock);10العامل ?.: الوصول الآمن
Section titled “العامل ?.: الوصول الآمن”إذا أردت خاصية أو دالة من متغير قد يكون null، استخدم ?. بدل النقطة العادية:
String? email;print(email?.length);print(email?.length);null16اقرأها: «إذا كان email موجوداً، أعطني طوله، وإلا أعطني null بهدوء». بدون انهيار.
الفحص بـ if
Section titled “الفحص بـ if”الطريقة الأوضح غالباً: افحص بنفسك. والجميل أن Dart تتذكر نتيجة الفحص:
String? title;if (title != null) { print(title.length); // هنا Dart تعرف أن title ليس null} else { print('لا يوجد عنوان');}لا يوجد عنوانداخل if (title != null)، تتعامل Dart مع title كأنه String عادي. هذه الميزة اسمها ترقية النوع (Type Promotion). ومثال عملي في دالة:
String describe(String name, int? age) { if (age == null) { return '$name: العمر غير معروف'; } return '$name: $age سنة';}
void main() { print(describe('سارة', null)); print(describe('عمر', 15));}سارة: العمر غير معروفعمر: 15 سنةالعامل !: «أنا متأكد»
Section titled “العامل !: «أنا متأكد»”العلامة ! بعد المتغير تقول لـ Dart: «أنا متأكد أن هذه القيمة ليست null، صدّقني»:
String? code = 'A1';String sure = code!;print(sure);A1لكن إذا كنت مخطئاً والقيمة null فعلاً، ينهار البرنامج بخطأ Null check operator used on a null value.
الكلمة late
Section titled “الكلمة late”أحياناً لا تستطيع إعطاء المتغير قيمة لحظة تعريفه، لكنك متأكد أنك ستعطيه قيمة قبل استخدامه:
late String message;message = 'تم التحميل';print(message);تم التحميلlate وعد منك: «سأملأ هذا المتغير قبل أن أقرأه». وإذا أخلفت الوعد وقرأته قبل إسناده، يظهر خطأ أثناء التشغيل. ستراها في Flutter أحياناً مع المتغيرات التي تُجهَّز عند بداية الشاشة.
جرّب بنفسك
Section titled “جرّب بنفسك”اكتب دالة greet(String? name) تُرجع «أهلاً يا …» بالاسم، أو «أهلاً يا ضيف» إذا كان الاسم null. جرّبها بثلاث طرق: بـ if، ثم بـ ??، ولاحظ كيف أصبح الكود أقصر.
String greet(String? name) => 'أهلاً يا ${name ?? 'ضيف'}';
void main() { print(greet('منى')); print(greet(null));}أهلاً يا منىأهلاً يا ضيفأخطاء شائعة
Section titled “أخطاء شائعة”- إسناد null لنوع عادي:
int count = null;خطأ. استخدمint?إذا كان الغياب ممكناً فعلاً. - الإفراط في استخدام !: يعيد مشاكل الانهيار التي جاءت Null Safety لتمنعها.
- جعل كل شيء يقبل null: إضافة
?لكل متغير «احتياطاً» تجبرك على فحوص في كل مكان. استخدم?فقط عندما يكون الغياب جزءاً حقيقياً من المعنى.
اختبر نفسك
Section titled “اختبر نفسك”سؤال
ما ناتج String? city; print(city ?? 'غير محدد');؟
city قيمته null، فيُرجع العامل ?? القيمة الاحتياطية «غير محدد».
سؤال
ماذا يحدث عند تنفيذ x! إذا كانت قيمة x تساوي null؟
! تعني «أنا متأكد أنها ليست null»، فإذا كانت null يظهر خطأ Null check operator.
الخلاصة
Section titled “الخلاصة”nullتعني غياب القيمة، والأنواع العادية في Dart لا تقبلها أبداً.String?نوع يقبل null، ويجبرك على التعامل مع احتمال الغياب.??لقيمة احتياطية، و??=للإسناد عند الغياب، و?.للوصول الآمن.- الفحص بـ
if (x != null)يرقّي النوع داخل الشرط. !تلغي الحماية فاستخدمها بحذر، وlateوعد بإسناد القيمة قبل قراءتها.