lateinit / lazy: Kotlin میں تاخیری ابتدا کا جوہر اور اس کے میکانزم

مصنف: IT Sectr اشاعت: 2026-06-23 مطالعے کا وقت: 8 منٹ

تاخیری ابتدا (lazy initialization) Kotlin میں ایک میکانزم ہے جس میں کسی آبجیکٹ کی پراپرٹی تخلیق کے لمحے میں نہیں، بلکہ پہلی بار رسائی پر ابتدا ہوتی ہے۔ JetBrains, 2024 کے مطابق، lateinit اور lazy اس حکمت عملی کو نافذ کرنے کے لیے دو بلٹ ان ٹولز ہیں۔ دونوں تاخیری ابتدا کے مسئلے کو حل کرتے ہیں، لیکن کام کرنے کے میکانزم اور اطلاق کے دائرہ کار میں بنیادی طور پر مختلف ہیں۔

خلاصہ

  • lateinit — var پراپرٹیز کے لیے ایک موڈیفائر، آبجیکٹ تخلیق کے بعد ابتدا کی اجازت دیتا ہے
  • lazy — val پراپرٹیز کے لیے ایک ڈیلیگیٹ، پہلی رسائی پر قدر کی ابتدا کرتا ہے
  • lateinit کے لیے var ضروری ہے اور یہ JVM قدیمی اقسام کو سپورٹ نہیں کرتا
  • lazy ڈیفالٹ طور پر تھریڈ سیف ہے اور حساب کردہ نتیجہ کو کیش کرتا ہے
  • lateinit قبل از وقت رسائی پر UninitializedPropertyAccessException پھینکتا ہے

Kotlin میں تاخیری ابتدا کیا ہے؟

تاخیری ابتدا ایک نمونہ ہے جس میں کلاس کی پراپرٹی آبجیکٹ کی تعمیر کے لمحے میں نہیں، بلکہ بعد میں، ضرورت پڑنے پر اپنی قدر حاصل کرتی ہے۔ Kotlin میں، یہ نمونہ دو بنیادی طور پر مختلف طریقوں سے نافذ کیا جاتا ہے: lateinit موڈیفائر اور lazy ڈیلیگیٹ۔

دونوں میکانزم ایک مشترکہ مسئلہ حل کرتے ہیں — ایک پراپرٹی کو کلاس میں موجود ہونا چاہیے، لیکن اس کی قدر یا تو آبجیکٹ تخلیق کے وقت نامعلوم ہوتی ہے، یا اس کا حساب بلا ضرورت انجام دینے کے لیے بہت زیادہ وسائل طلب ہوتا ہے۔ Google I/O 2023 کے مطابق، ایک عام Android ایپلیکیشن میں 40% تک پراپرٹیز کو تاخیری ابتدا کے ذریعے بہتر بنایا جا سکتا ہے، جس سے شروع ہونے کا وقت 15–25% کم ہو جاتا ہے۔

lateinit اور lazy کے درمیان انتخاب تین عوامل سے طے ہوتا ہے: پراپرٹی کی تبدیلی پذیری (var یا val)، اس کا لائف سائیکل (واحد یا متعدد اسائنمنٹ)، اور تھریڈ سیفٹی کی ضروریات (واحد تھریڈ یا کثیر تھریڈ رسائی)۔

تاخیری ابتدا کب استعمال ہوتی ہے

پہلا اور سب سے عام منظر نامہ انحصار انجیکشن ہے۔ فریم ورک (Dagger, Hilt, Koin) آبجیکٹ تخلیق کے بعد انحصار انجیکٹ کرتا ہے، لہذا پراپرٹی کنسٹرکٹر میں ابتدا نہیں کی جا سکتی۔ lateinit کے بغیر، تمام انحصاروں کو nullable قرار دینا اور ہر استعمال پر جانچ کرنا پڑے گا۔

دوسرا منظر نامہ بھاری وسائل ہیں: ڈیٹا بیس، نیٹ ورک کلائنٹ، فائل مینیجر۔ ان کی تخلیق میں وقت اور میموری لگتی ہے، لہذا انہیں صرف حقیقی استعمال پر ہی ابتدا کرنا چاہیے۔ lazy ایسے معاملات کے لیے مثالی ہے، جو واحد تخلیق کی ضمانت دیتا ہے۔

تیسری صورت حال Android اجزاء (Activity, Fragment, ViewModel) ہیں، جن کا لائف سائیکل آپریٹنگ سسٹم کے زیر انتظام ہوتا ہے۔ جو پراپرٹیز onCreate، onViewCreated یا ViewModel کے init بلاک پر منحصر ہوتی ہیں، انہیں کنسٹرکٹر میں ابتدا نہیں کیا جا سکتا۔

lateinit: میکانزم اور حدود

lateinit var پراپرٹیز کے لیے ایک موڈیفائر ہے جو Kotlin کمپائلر کو ابتدا مؤخر کرنے کی اجازت دیتا ہے۔ کمپائلر کنسٹرکٹر میں قدر تفویض کی ضرورت نہیں رکھتا، لیکن ہر رسائی پر رن ٹائم جانچ پیدا کرتا ہے: اگر پراپرٹی ابتدا نہیں ہوئی، تو یہ UninitializedPropertyAccessException پھینکتا ہے۔

kotlin
class MainActivity {
    lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
    }
}

lateinit کی حدود: پراپرٹی کو var (val نہیں)، نان نیل ایبل، اور قدیمی قسم (Int, Double, Boolean وغیرہ) نہیں قرار دینا چاہیے۔ وجہ یہ ہے کہ قدیمی اقسام JVM قدیم میں کمپائل ہوتی ہیں، جن کی کوئی “ابتداء نہیں ہوئی” حالت نہیں ہوتی۔ نیل ایبل پراپرٹیز کے لیے، تاخیری ابتدا ضروری نہیں ہے: null پہلے ہی قدر کی عدم موجودگی کو ظاہر کرتا ہے۔

lateinit پراپرٹی کی حالت جاننے کے لیے، :: آپریٹر کے ذریعے بلٹ ان ریفرنس استعمال کریں: ::propertyName.isInitialized۔ بغیر کسی مستثنیٰ کے خطرے کے پراپرٹی کی ابتدا ہونے کی جانچ کرنے کا یہ واحد محفوظ طریقہ ہے۔ یہ جانچ صرف اسی کلاس یا اندرونی کلاس سے دستیاب ہے، بیرونی کوڈ سے نہیں۔

kotlin
class LoginFragment {
    lateinit var binding: FragmentLoginBinding

    fun isReady(): Boolean {
        return ::binding.isInitialized
    }
}

lateinit کی کارکردگی

lateinit ابتدا کے بعد کوئی اضافی بوجھ نہیں ڈالتا: قدر تفویض ہونے کے بعد، پراپرٹی تک رسائی براہ راست فیلڈ تک رسائی کے مترادف ہے۔ واحد لاگت تفویض سے پہلے ہر پڑھائی پر ابتداء کی جانچ ہے۔ ابتدا کے بعد، JIT کمپائلر جانچ کو بہتر بنا دیتا ہے۔

ایک اہم نوٹ: lateinit پراپرٹیز ان لائن کلاسز میں استعمال نہیں کی جا سکتیں اور کسٹم getter/setter والی پراپرٹیز کے لیے معاونت یافتہ نہیں ہیں۔ اگر کسی پراپرٹی کو حسابی رسائی کی ضرورت ہے، تو lateinit کی بجائے lazy استعمال کریں۔

lazy: میکانزم اور فوائد

lazy Kotlin معیاری لائبریری میں شامل ایک پراپرٹی ڈیلیگیٹ ہے۔ یہ پراپرٹی تک پہلی رسائی پر قدر کا حساب لگاتا ہے اور بعد کی تمام کالوں کے لیے نتیجہ کیش کرتا ہے۔ lateinit کے برعکس، lazy صرف val کے ساتھ کام کرتا ہے، ابتدا کے بعد پراپرٹی کو ناقابل تبدیلی بنا دیتا ہے۔

kotlin
class UserRepository {
    private val database: Database by lazy {
        Database.create("users.db")
    }

    fun getUser(id: String): User {
        return database.query("SELECT * FROM users WHERE id = ?", id)
    }
}

lazy ایک اختیاری پیرامیٹر LazyThreadSafetyMode قبول کرتا ہے جو تھریڈ سیفٹی میکانزم کو کنٹرول کرتا ہے۔ ڈیفالٹ SYNCHRONIZED ہے — لاکنگ کے ساتھ دوہری جانچ، جو متعدد تھریڈز سے بیک وقت رسائی کے تحت بھی واحد ابتدا کی ضمانت دیتا ہے۔

lazy کے تھریڈ سیفٹی موڈز

PUBLICATION موڈ متوازی ابتدا کی اجازت دیتا ہے: متعدد تھریڈ بیک وقت ابتدا بلاک پر عمل کر سکتے ہیں، لیکن نتیجہ صرف پہلے مکمل کرنے والے تھریڈ سے قبول کیا جاتا ہے۔ یہ زیادہ مسابقت میں SYNCHRONIZED سے تیز ہے، لیکن وسائل کی کھپت بڑھاتا ہے۔

NONE موڈ ہم آہنگی کو مکمل طور پر غیر فعال کر دیتا ہے۔ اسے صرف ان پراپرٹیز کے لیے استعمال کریں جن تک رسائی ایک ہی تھریڈ سے یقینی ہو۔ اس موڈ میں، lazy کم سے کم اضافی بوجھ کے ساتھ کام کرتا ہے — تقریباً براہ راست تفویض کی طرح۔

kotlin
val heavyConfig: Config by lazy(LazyThreadSafetyMode.NONE) {
    Config.loadFromFile("config.json")
}

lazy کب ترجیح دیتا ہے

lazy ایک بار ابتدا ہونے والے انحصاروں کے لیے صحیح انتخاب ہے: ذخیرے، نیٹ ورک کلائنٹ، کیشے، ڈیٹا بیس۔ val سیمینٹکس حادثاتی طور پر اوور رائٹنگ سے بچاتا ہے، اور ڈیفالٹ تھریڈ سیفٹی کوڈ کو کثیر تھریڈ والے ماحول میں محفوظ بناتی ہے۔ lazy قدیمی اقسام کے ساتھ بھی درست طریقے سے کام کرتا ہے، جو lateinit کے ساتھ ناممکن ہے۔

Android میں، lazy اکثر by viewModels() کے ذریعے ViewModel انحصار کی ابتدا کرنے یا Retrofit کلائنٹ بنانے کے لیے استعمال ہوتا ہے۔ تاہم، محتاط رہیں: اگر lazy بلاک Activity یا Fragment کا حوالہ پکڑتا ہے، تو یہ میموری لیک کا سبب بن سکتا ہے، کیونکہ ڈیلیگیٹ پراپرٹی کی زندگی بھر بندش کو برقرار رکھتا ہے۔

lateinit بمقابلہ lazy: طریقوں کا موازنہ

lateinit اور lazy کے درمیان انتخاب ترجیح کا معاملہ نہیں ہے، بلکہ ایک معماری فیصلہ ہے جو پراپرٹی کی نوعیت سے طے ہوتا ہے۔ ہر میکانزم اپنا کام حل کرتا ہے، اور ان کے اطلاق کے شعبے صرف جزوی طور پر ایک دوسرے کو چھوتے ہیں۔

معیارlateinitlazy
پراپرٹی کی قسمصرف varصرف val
Nullableاجازت نہیںاجازت ہے
قدیمی اقساماجازت نہیںاجازت ہے
تھریڈ سیفٹیضمانت نہیںڈیفالٹ SYNCHRONIZED
حالت کی جانچ::x.isInitializedضروری نہیں
غلطی پر مستثنیٰUninitializedPropertyAccessExceptioninit بلاک میں خرابی
کیشنگلاگو نہیںواحد حساب
Android BindingView Binding, Data Bindingاستعمال نہیں ہوتا
DI فریم ورکDagger, Hilt, Koinدستی انجیکشن

lateinit استعمال کریں جب پراپرٹی کو ابتدا کے بعد تبدیل ہونا ہو یا اس کی تخلیق بیرونی کوڈ کے زیر انتظام ہو۔ ایک عام مثال Android Activity میں View Binding ہے: binding onCreate میں بنایا جاتا ہے لیکن var رہتا ہے کیونکہ فریم ورک اس منظر نامے کے لیے val کو سپورٹ نہیں کرتا۔

lazy استعمال کریں جب پراپرٹی ایک بار ابتدا ہوتی ہے، اس کا حساب مہنگا ہو، اور آبجیکٹ کی زندگی میں قدر تبدیل نہ ہو۔ ایک کلاسک مثال ذخیرے تک پہلی رسائی پر Retrofit کلائنٹ یا Room ڈیٹا بیس کی سست تخلیق ہے۔

lateinit اور lazy کا امتزاج

دونوں میکانزم ایک ہی کلاس میں بیک وقت استعمال کیے جا سکتے ہیں۔ مثال کے طور پر، View Binding کے لیے lateinit اور ذخیرے کے لیے lazy۔ یہ ایک عام عمل ہے جو مختلف پراپرٹیز کے لیے مختلف تقاضوں کی عکاسی کرتا ہے۔ اہم بات یہ ہے کہ سیمینٹکس میں الجھن پیدا نہ کریں: جہاں val ضروری ہو وہاں lateinit استعمال نہ کریں، اور جن پراپرٹیز کو دوبارہ تفویض کرنے کی ضرورت ہو ان کے لیے lazy استعمال نہ کریں۔

lateinit اور lazy استعمال کرتے وقت عام غلطیاں

lateinit کے ساتھ سب سے عام غلطی پراپرٹی تک اس کی ابتدا سے پہلے رسائی ہے۔ یہ UninitializedPropertyAccessException کا باعث بنتی ہے، جو مرتب وقت پر نہیں پکڑی جاتی کیونکہ Kotlin ڈویلپر پر صحیح ابتدا کے سلسلے کو یقینی بنانے کا بھروسہ کرتا ہے۔ حل یہ ہے کہ غیر واضح حالات میں رسائی سے پہلے ::property.isInitialized کے ذریعے ہمیشہ حالت جانچیں۔

دوسرا عام مسئلہ سمینٹکس کے لحاظ سے val پراپرٹیز کے لیے lateinit استعمال کرنا ہے۔ اگر قدر ایک بار مقرر ہوتی ہے اور کبھی تبدیل نہیں ہوتی، تو lazy زیادہ درست انتخاب ہے۔ یہ پراپرٹی کو ناقابل تبدیلی بناتا ہے، حادثاتی اوور رائٹنگ کو روکتا ہے، اور مفت میں تھریڈ سیفٹی شامل کرتا ہے۔

تیسری غلطی ضمنی اثرات والا lazy ہے۔ lazy ابتدا بلاک کو بیرونی حالت میں تبدیلی نہیں کرنی چاہیے یا دوسری lazy پراپرٹی کے ابتدا کے سلسلے پر منحصر نہیں ہونا چاہیے، کیونکہ حساب کا سلسلہ پہلی رسائی پر منحصر ہوتا ہے اور واضح نہیں ہو سکتا۔ اگر lazy پراپرٹی ایک دوسرے کا حوالہ دیتی ہیں، تو یہ چکری انحصار اور StackOverflowError کا باعث بنتی ہے۔

چوتھا مسئلہ Android میں lazy کے ذریعے میموری لیک ہے۔ اگر lazy بلاک Activity یا Fragment کا حوالہ پکڑتا ہے، تو ڈیلیگیٹ بندش کو برقرار رکھتا ہے اور کوڑا کرکٹ جمع کرنے والا جزو تباہی کے بعد بھی اسے آزاد نہیں کر سکتا۔ حل یہ ہے کہ lazy صرف مختصر عمر کی آبجیکٹ کے ساتھ استعمال کریں یا Activity کی بجائے Application سیاق پاس کریں۔

پانچویں عام غلطی قدیمی اقسام پر lateinit لاگو کرنے کی کوشش ہے۔ Kotlin کمپائلر اسے نحوی سطح پر روکتا ہے، لیکن ڈویلپر نیل ایبل ریپرز کے ذریعے حد کو عبور کرنے کی کوشش کرتے ہیں۔ اس سے غیر ضروری null جانچیں ہوتی ہیں اور تاخیری ابتدا کے فوائد مکمل طور پر ختم ہو جاتے ہیں۔

اکثر پوچھے گئے سوالات

Kotlin میں lateinit اور lazy میں کیا فرق ہے؟

lateinit var پراپرٹیز کے لیے ایک موڈیفائر ہے، جو کنسٹرکٹر کے بعد ابتدا کی اجازت دیتا ہے۔ lazy val پراپرٹیز کے لیے ایک ڈیلیگیٹ ہے، جو پہلی رسائی پر قدر کا حساب لگاتا ہے اور کیش کرتا ہے۔ lateinit قدیمی اقسام اور nullable کو سپورٹ نہیں کرتا، جبکہ lazy ڈیفالٹ طور پر تھریڈ سیف ہے۔

کیا میں جانچ سکتا ہوں کہ lateinit پراپرٹی کی ابتدا ہوئی ہے؟

ہاں، بلٹ ان پراپرٹی ریفرنس کے ذریعے: ::propertyName.isInitialized۔ اگر پراپرٹی پہلے ہی ابتدا ہو چکی ہے تو طریقہ true لوٹاتا ہے۔ lateinit فیلڈز کے ساتھ کام کرتے ہوئے UninitializedPropertyAccessException سے بچنے کا یہ واحد محفوظ طریقہ ہے۔

lateinit قدیمی اقسام کے ساتھ کیوں استعمال نہیں کیا جا سکتا؟

قدیمی اقسام — Int, Double, Boolean اور دیگر — JVM قدیم (int, double, boolean) میں کمپائل ہوتی ہیں، جن کی کوئی “ابتداء نہیں ہوئی” حالت نہیں ہوتی۔ lateinit null کو بطور علامت استعمال کرتا ہے، اور قدیم null نہیں ہو سکتے، لہذا یہ میکانزم ان اقسام کے لیے طبعی طور پر ناممکن ہے۔

lazy کا ڈیفالٹ تھریڈ سیفٹی موڈ کیا ہے؟

ڈیفالٹ LazyThreadSafetyMode.SYNCHRONIZED ہے — لاکنگ کے ساتھ دوہری جانچ، جو متعدد تھریڈز سے بیک وقت رسائی کے تحت واحد ابتدا کی ضمانت دیتا ہے۔ واحد تھریڈ منظرناموں کے لیے NONE، زیادہ مسابقت کے لیے PUBLICATION استعمال کریں۔

Android میں lateinit کی بجائے lazy کب استعمال کرنا چاہیے؟

جب پراپرٹی کو ابتدا کے بعد تبدیل ہونا ہو یا اس کی تخلیق فریم ورک کے زیر انتظام ہو۔ ایک عام مثال Android Activity میں View Binding ہے: binding onCreate میں بنایا جاتا ہے اور اسے var ہونا چاہیے۔ ایک بار ابتدا ہونے والے val انحصاروں کے لیے lazy استعمال کریں۔

خلاصہ

  • lateinit — Kotlin var پراپرٹیز کے لیے ایک موڈیفائر، nullable کے بغیر کنسٹرکٹر کے بعد ابتدا کی اجازت دیتا ہے
  • lazy — val کے لیے ایک پراپرٹی ڈیلیگیٹ، واحد حساب اور خودکار نتیجہ کیشنگ کے ساتھ
  • lateinit ابتدا سے پہلے رسائی پر UninitializedPropertyAccessException پھینکتا ہے
  • lazy LazyThreadSafetyMode.SYNCHRONIZED کے ذریعے ڈیفالٹ طور پر تھریڈ سیف ہے
  • lateinit قدیمی اقسام اور nullable پراپرٹیز کے ساتھ مطابقت نہیں رکھتا
  • lazy بندش میں سیاق پکڑنے پر Android میں میموری لیک کا سبب بن سکتا ہے
  • تبدیلی پذیر پراپرٹیز کے لیے lateinit اور ایک بار ابتدا ہونے والے val انحصاروں کے لیے lazy منتخب کریں

ہم ایک موبائل ایپلیکیشن ٹرنکی تیار کریں گے

IT Sectr 2017 سے اسٹارٹ اپس اور کاروبار کے لیے iOS اور Android ایپلیکیشنز بناتا ہے۔ ہم آپ کو مشورہ دیں گے اور بہترین حل تجویز کریں گے۔

پروجیکٹ پر بحث کریں

مزید پڑھیں