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

الكلمة المفتاحية Late وأمان الـ Null

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

أحياناً تحتاج إلى متغير غير قابل للقيمة الفارغة (non-nullable)، ولكن لا يمكنك تعيين قيمته على الفور. تخبر الكلمة المفتاحية late لغة Dart "أعدك بتهيئة هذا قبل استخدامه".

late String description;

void setup() {
  description = 'Initialized later';
}

void main() {
  setup();
  print(description);  // يعمل - تم تهيئته قبل الاستخدام
}

بدون late، سيشتكي Dart من أن description لم يتم تهيئته. مع late، أنت تتحمل مسؤولية التأكد من أنه يحتوي على قيمة قبل الوصول إليه. إذا حاولت قراءته قبل التهيئة، فستحصل على خطأ وقت التشغيل.

حالة استخدام شائعة هي العمليات الحسابية المكلفة التي تريد تأجيلها حتى الحاجة إليها فعلياً:

class DataProcessor {
  late List<int> data = _loadData();
  
  List<int> _loadData() {
    print('Loading data...');
    return [1, 2, 3, 4, 5];
  }
}

DataProcessor processor = DataProcessor();
// لم يتم طباعة 'Loading data...' بعد
print(processor.data);  // الآن يتم التحميل والطباعة

تعمل الطريقة _loadData() فقط عندما يتم الوصول إلى data لأول مرة، وليس عند إنشاء الكائن. يمكن لهذا التهيئة الكسولة (lazy initialization) تحسين الأداء عندما لا تكون هناك حاجة للقيمة أبدًا.

استخدم late عندما تكون متأكداً من أن المتغير سيتم تهيئته قبل الاستخدام ولكن لا يمكنك القيام بذلك عند التصريح عنه. إنه بمثابة وعد للمترجم - وإذا أخلفت هذا الوعد، فسينهار برنامجك.

challenge icon

التحدي

سهل

لنقم ببناء مدير تكوين (configuration manager) يوضح قوة متغيرات late للتهيئة المؤجلة. ستقوم بإنشاء نظام حيث لا تحدث عمليات الإعداد المكلفة إلا عندما تكون هناك حاجة فعلية للتكوين.

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

  • config_manager.dart: قم بتعريف فئة ConfigManager التي تتعامل مع إعدادات التطبيق. يجب أن تحتوي الفئة على:
    • String appName يتم تمريره عبر المنشئ (constructor)
    • late String databaseUrl سيتم تهيئته لاحقاً عبر طريقة إعداد (setup method)
    • late int maxConnections مع تهيئة كسولة (lazy initialization) تطبع Calculating max connections... عند الوصول إليها لأول مرة وتعيد 100
    • طريقة setupDatabase(String url) تقوم بتهيئية databaseUrl
    • طريقة displayConfig() تطبع التكوين الحالي
  • main.dart: قم باستيراد مدير التكوين الخاص بك ووضح كيفية عمل متغيرات late:
    • قم بإنشاء ConfigManager مع اسم تطبيق 'MyApp'
    • اطبع Config created
    • استدعِ setupDatabase مع عنوان URL هو 'localhost:5432/mydb'
    • اطبع Accessing max connections...
    • اطبع قيمة maxConnections
    • اطبع Accessing max connections again...
    • اطبع قيمة maxConnections مرة أخرى (لاحظ أن رسالة الحساب لا تتكرر)
    • استدعِ displayConfig()

يجب أن تطبع طريقة displayConfig() بهذا التنسيق:

--- Configuration ---
App: [appName]
Database: [databaseUrl]
Max Connections: [maxConnections]

لاحظ كيف أن حساب maxConnections الكسول يعمل مرة واحدة فقط - في المرة الأولى التي تصل فيها إليه. هذه هي فائدة late مع الميئ (initializer)!

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

Config created
Accessing max connections...
Calculating max connections...
100
Accessing max connections again...
100
--- Configuration ---
App: MyApp
Database: localhost:5432/mydb
Max Connections: 100

ورقة مرجعية

تسمح لك الكلمة الأساسية late بالتصريح عن متغيرات غير قابلة للقيمة الخالية (non-nullable) سيتم تهيئتها لاحقاً، قبل استخدامها:

late String description;

void setup() {
  description = 'Initialized later';
}

void main() {
  setup();
  print(description);  // يعمل - تم تهيئته قبل الاستخدام
}

بدون late، تتطلب Dart تهيئة فورية. مع late، أنت تعد بتهيئة المتغير قبل الوصول إليه. قراءة متغير late غير مهيأ تؤدي إلى حدوث خطأ في وقت التشغيل (runtime error).

التهيئة الكسولة (Lazy Initialization): عندما يحتوي متغير late على قيمة تهيئة، يتم تأجيل التهيئة حتى يتم الوصول إلى المتغير لأول مرة:

class DataProcessor {
  late List<int> data = _loadData();
  
  List<int> _loadData() {
    print('Loading data...');
    return [1, 2, 3, 4, 5];
  }
}

DataProcessor processor = DataProcessor();
// 'Loading data...' لم تُطبع بعد
print(processor.data);  // الآن يتم التحميل والطباعة

يتم تشغيل الطريقة _loadData() فقط عند الوصول إلى data لأول مرة، وليس عند إنشاء الكائن. يؤدي هذا إلى تحسين الأداء عندما لا تكون هناك حاجة للقيمة أبداً أو عندما تكون التهيئة مكلفة.

جرّب بنفسك

import 'config_manager.dart';

void main() {
  // TODO: أنشئ ConfigManager باسم تطبيق 'MyApp'
  
  // TODO: اطبع 'Config created'
  
  // TODO: استدعِ setupDatabase بالرابط 'localhost:5432/mydb'
  
  // TODO: اطبع 'Accessing max connections...'
  
  // TODO: اطبع قيمة maxConnections
  
  // TODO: اطبع 'Accessing max connections again...'
  
  // TODO: اطبع قيمة maxConnections مرة أخرى
  // (لاحظ أن رسالة الحساب لا تتكرر)
  
  // TODO: استدعِ displayConfig()
}
quiz iconاختبر نفسك

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

جميع دروس Object Oriented Programming