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

Nullable مقابل Non-Nullable

جزء من قسم Object Oriented Programming في رحلة Dart على Coddy — الدرس 24 من 110.

الآن بعد أن فهمت سبب أهمية سلامة القيم الخالية (null safety)، دعنا نلقي نظرة على كيفية التصريح عن المتغيرات التي يمكن أن تحمل قيمة خالية (null). في لغة Dart، يمكنك جعل النوع قابلاً للقيمة الخالية (nullable) عن طريق إضافة ? بعد اسم النوع.

String name = 'Alice';   // غير قابل للقيمة الخالية - يجب أن يحتوي دائمًا على قيمة
String? nickname;        // قابل للقيمة الخالية - يمكن أن يكون null أو String

print(name);      // Alice
print(nickname);  // null

الفرق الجوهري هو أن الأنواع غير القابلة للقيمة null تضمن وجود قيمة، بينما تسمح الأنواع القابلة للقيمة null صراحةً بغياب القيمة. ينطبق هذا التمييز على جميع الأنواع:

int count = 10;       // يجب أن يحتوي على قيمة
int? optionalCount;   // يمكن أن يكون null

List<String> items = [];      // يجب أن تكون قائمة (يمكن أن تكون فارغة)
List<String>? maybeItems;     // يمكن أن يكون null (لا توجد قائمة على الإطلاق)

لاحظ أن القائمة الفارغة [] تختلف عن null. القائمة الفارغة موجودة ولكنها لا تحتوي على شيء؛ null تعني عدم وجود قائمة على الإطلاق.

يفرض المترجم هذه القواعد بصرامة. لا يمكنك تعيين null لمتغير غير قابل للقيمة null (non-nullable)، ولا يمكنك استخدام متغير قابل للقيمة null (nullable) حيث يُتوقع وجود متغير غير قابل للقيمة null دون التحقق أولاً من القيمة null:

String? maybeName = 'Bob';
// String definite = maybeName;  // خطأ! لا يمكن تعيين قيمة قابلة للإلغاء (nullable) إلى قيمة غير قابلة للإلغاء (non-nullable)

في الدرس القادم، ستتعلم المعاملات التي توفرها لغة Dart للعمل بأمان مع القيم التي تقبل القيمة الخالية (nullable values).

challenge icon

التحدي

سهل

لنقم ببناء نظام قائمة مشاهدة أفلام يوضح الفرق بين المعلومات المطلوبة والمعلومات الاختيارية باستخدام الأنواع القابلة للإلغاء (nullable) وغير القابلة للإلغاء (non-nullable).

ستقوم بإنشاء ملفين لتنظيم الكود الخاص بك:

  • movie.dart: قم بتعريف فئة Movie تمثل فيلماً في قائمة المشاهدة الخاصة بك. يجب أن يكون لكل فيلم عنوان وسنة إصدار (هذه مطلوبة دائماً)، ولكن قد لا تتوفر بعض التفاصيل الإضافية لكل فيلم. يجب أن تحتوي الفئة الخاصة بك على:
    • متغير String title غير قابل للإلغاء - يجب أن يكون لكل فيلم عنوان
    • متغير int year غير قابل للإلغاء - يجب أن يكون لكل فيلم سنة إصدار
    • متغير String? director قابل للإلغاء - قد يكون المخرج غير معروف
    • متغير double? rating قابل للإلغاء - لم يتم تقييم جميع الأفلام بعد
    • منشئ (constructor) يأخذ title و year كمعاملات مطلوبة
    • دالة displayInfo() تقوم بطباعة معلومات الفيلم، مع إظهار "Unknown" للمخرج المفقود و "Not rated" للتقييم المفقود
  • main.dart: قم باستيراد فئة الفيلم الخاصة بك وإنشاء قائمة مشاهدة صغيرة توضح كلاً من بيانات الأفلام الكاملة وغير الكاملة:
    • قم بإنشاء فيلم بالعنوان 'Inception' والسنة 2010، ثم قم بتعيين المخرج الخاص به إلى 'Christopher Nolan' والتقييم إلى 8.8
    • قم بإنشاء فيلم بالعنوان 'The Matrix' والسنة 1999، ثم قم بتعيين تقييمه فقط إلى 8.7 (اترك المخرج كـ null)
    • قم بإنشاء فيلم بالعنوان 'Upcoming Film' والسنة 2025 (اترك كلاً من المخرج والتقييم كـ null)
    • قم باستدعاء displayInfo() لكل فيلم بالترتيب المذكور أعلاه

يجب أن تقوم الدالة displayInfo() بالطباعة بهذا التنسيق الدقيق:

[title] ([year])
Director: [director or "Unknown"]
Rating: [rating or "Not rated"]

لاحظ كيف أن الحقول غير القابلة للإلغاء (title و year) مضمونة دائماً بوجود قيم، بينما الحقول القابلة للإلغاء (director? و rating?) يمكن أن تظل غير محددة. هذا يعكس سيناريوهات العالم الحقيقي حيث تكون بعض البيانات أساسية وبعضها اختيارياً.

المخرجات المتوقعة:

Inception (2010)
Director: Christopher Nolan
Rating: 8.8
The Matrix (1999)
Director: Unknown
Rating: 8.7
Upcoming Film (2025)
Director: Unknown
Rating: Not rated

ورقة مرجعية

لجعل النوع قابلاً للقيمة الفارغة (nullable) في لغة Dart، أضف ? بعد اسم النوع:

String name = 'Alice';   // Non-nullable - must always have a value
String? nickname;        // Nullable - can be null or a String

ينطبق هذا التمييز على جميع الأنواع:

int count = 10;       // Must have a value
int? optionalCount;   // Can be null

List<String> items = [];      // Must be a list (can be empty)
List<String>? maybeItems;     // Could be null (no list at all)

القائمة الفارغة [] تختلف عن null. القائمة الفارغة موجودة ولكنها لا تحتوي على شيء؛ أما null فتعني عدم وجود قائمة على الإطلاق.

يفرض المترجم (compiler) قواعد سلامة القيم الفارغة (null safety) بصرامة. لا يمكنك تعيين null لمتغير غير قابل للقيمة الفارغة (non-nullable)، ولا يمكنك استخدام متغير قابل للقيمة الفارغة حيث يُتوقع وجود متغير غير قابل للقيمة الفارغة دون التحقق أولاً من القيمة الفارغة:

String? maybeName = 'Bob';
// String definite = maybeName;  // Error! Can't assign nullable to non-nullable

جرّب بنفسك

import 'movie.dart';

void main() {
  // TODO: قم بإنشاء فيلم بالعنوان 'Inception' والسنة 2010
  // ثم قم بتعيين director الخاص به إلى 'Christopher Nolan' و rating إلى 8.8
  
  // TODO: قم بإنشاء فيلم بالعنوان 'The Matrix' والسنة 1999
  // ثم قم بتعيين rating الخاص به فقط إلى 8.7 (اترك director كـ null)
  
  // TODO: قم بإنشاء فيلم بالعنوان 'Upcoming Film' والسنة 2025
  // اترك كل من director و rating كـ null
  
  // TODO: قم باستدعاء displayInfo() لكل فيلم بالترتيب
}
quiz iconاختبر نفسك

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

جميع دروس Object Oriented Programming