lateinit / lazy: kechiktirilgan initsializatsiyaning mohiyati va mexanizmlari Kotlin-da

Muallif: IT Sectr Nashr etilgan: 2026-06-23 O'qish vaqti: 8 daq

Kechiktirilgan initsializatsiya (lazy initialization) — Kotlin-dagi ob'ekt xususiyati yaratilish paytida emas, balki unga birinchi murojaat qilinganda initsializatsiya qilinadigan mexanizm. JetBrains, 2024 ma'lumotlariga ko'ra, lateinit va lazy bu strategiyani amalga oshirish uchun ikkita o'rnatilgan vositadir. Ikkalasi ham initsializatsiyani kechiktirish muammosini hal qiladi, lekin ishlash mexanizmi va qo'llanish sohasi bo'yicha printsipial farqlanadi.

Asosiy

  • lateinit — ob'ekt yaratilgandan keyin initsializatsiyaga ruxsat beruvchi var-xususiyatlar uchun modifikator
  • lazy — birinchi murojaatda qiymatni initsializatsiya qiluvchi val-xususiyatlar uchun delegat
  • lateinit var talab qiladi va JVM primitiv turlarini qo'llab-quvvatlamaydi
  • lazy standart sifatida thread-safe va hisoblangan natijani keshlaydi
  • lateinit muddatidan oldin kirishda UninitializedPropertyAccessException chiqaradi

Kotlin-da kechiktirilgan initsializatsiya nima?

Kechiktirilgan initsializatsiya — bu sinf xususiyati qiymatni ob'ekt qurilish paytida emas, balki keyinroq, talab bo'yicha oladigan namuna. Kotlin-da bu namuna ikki printsipial jihatdan farqli usulda amalga oshiriladi: lateinit modifikatori va lazy delegati.

Ikkala mexanizm umumiy muammoni hal qiladi — xususiyat sinfda mavjud bo'lishi kerak, lekin uning qiymati yoki ob'ekt yaratish paytida hali ma'lum emas, yoki uni hisoblash keraksiz bajarish uchun juda resurs talab qiladi. Google I/O 2023 ma'lumotlariga ko'ra, odatdagi Android ilovasidagi xususiyatlarning 40% gacha qismi kechiktirilgan initsializatsiya orqali optimallashtirilishi mumkin, bu esa ishga tushirish vaqtini 15–25% ga kamaytiradi.

lateinit va lazy o'rtasidagi tanlov uch omil bilan belgilanadi: xususiyatning o'zgaruvchanligi (var yoki val), uning hayot vaqti (bir martalik yoki ko'p martalik tayinlash) va thread-safe talablari (bir ipli yoki ko'p ipli kirish).

Kechiktirilgan initsializatsiya qachon qo'llaniladi

Birinchi va eng keng tarqalgan stsenariy — Dependency Injection. Freymvork (Dagger, Hilt, Koin) ob'ekt yaratilgandan keyin bog'liqliklarni injekt qiladi, shuning uchun xususiyat konstruktorda initsializatsiya qilinishi mumkin emas. lateinit bo'lmasa, barcha bog'liqliklarni nullable deb e'lon qilish va har foydalanishda ularni tekshirish kerak bo'ladi.

Ikkinchi stsenariy — og'ir resurslar: ma'lumotlar bazasi, tarmoq klienti, fayl menejeri. Ularni yaratish vaqt va xotira talab qiladi, shuning uchun ular faqat haqiqiy foydalanishda initsializatsiya qilinishi kerak. lazy bunday holatlar uchun ideal bo'lib, bir martalik yaratishni kafolatlaydi.

Uchinchi vaziyat — Android komponentlari (Activity, Fragment, ViewModel), ularning hayot tsikli operatsion tizim tomonidan boshqariladi. onCreate, onViewCreated yoki ViewModel init blokiga bog'liq bo'lgan xususiyatlar konstruktorda initsializatsiya qilinishi mumkin emas.

lateinit: mexanizm va cheklovlar

lateinit — Kotlin kompilyatoriga initsializatsiyani kechiktirishga ruxsat beruvchi var-xususiyatlar uchun modifikator. Kompilyator konstruktorda qiymat tayinlashni talab qilmaydi, lekin har bir kirishda runtime tekshiruvi yaratadi: agar xususiyat initsializatsiya qilinmagan bo'lsa, UninitializedPropertyAccessException chiqariladi.

kotlin
class MainActivity {
    lateinit var binding: ActivityMainBinding

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

lateinit cheklovlari: xususiyat var (val emas), nullable bo'lmagan, primitiv turdagi (Int, Double, Boolean va boshqalar) bo'lmagan holda e'lon qilinishi kerak. Sababi — primitiv turlar JVM primitivlariga kompilyatsiya qilinadi, ularning «initsializatsiya qilinmagan» holati yo'q. Nullable xususiyatlar uchun kechiktirilgan initsializatsiya kerak emas: null allaqachon qiymat yo'qligini anglatadi.

lateinit xususiyatining holatini tekshirish uchun :: operatori orqali o'rnatilgan havola ishlatiladi: ::propertyName.isInitialized. Bu istisno olish xavfisiz xususiyatning initsializatsiya qilinganligini tekshirishning yagona xavfsiz usuli. Tekshiruv faqat bir xil sinfdan yoki ichki sinfdan mavjud, tashqi koddan emas.

kotlin
class LoginFragment {
    lateinit var binding: FragmentLoginBinding

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

lateinit ishlashi

lateinit initsializatsiyadan keyin qo'shimcha yuk qo'shmaydi: qiymat tayinlangandan so'ng xususiyatga kirish to'g'ridan-to'g'ri maydonga murojaat bilan bir xil. Yagona xarajat — tayinlashdan oldin har bir o'qishda initsializatsiya tekshiruvi. Initsializatsiyadan keyin JIT kompilyatori tekshiruvni optimallashtiradi.

Muhim xususiyat: lateinit xususiyatlari inline sinflarda ishlatilmaydi va maxsus getter/setter bo'lgan xususiyatlar uchun qo'llab-quvvatlanmaydi. Agar xususiyat hisoblash kirishini talab qilsa — lateinit o'rniga lazy ishlating.

lazy: mexanizm va afzalliklar

lazy — Kotlin standart kutubxonasiga o'rnatilgan xususiyat delegati. U xususiyatga birinchi murojaatda qiymatni hisoblaydi va natijani barcha keyingi chaqiruvlar uchun keshlaydi. lateinit-dan farqli o'laroq, lazy faqat val bilan ishlaydi va xususiyatni initsializatsiyadan keyin o'zgarmas qiladi.

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 thread-safe mexanizmini boshqaradigan ixtiyoriy LazyThreadSafetyMode parametrini qabul qiladi. Standart sifatida SYNCHRONIZED ishlatiladi — ikki marta tekshirish blokirovkasi (Double-Checked Locking), bir nechta iplardan bir vaqtda kirishda ham bir martalik initsializatsiyani kafolatlaydi.

lazy thread-safe rejimlari

PUBLICATION rejimi parallel initsializatsiyaga ruxsat beradi: bir nechta iplar bir vaqtning o'zida initsializatsiya blokini bajarishi mumkin, ammo natija faqat birinchi tugatganidan qabul qilinadi. Bu yuqori raqobatda SYNCHRONIZED-dan tezroq, ammo resurs sarfini oshiradi.

NONE rejimi sinxronizatsiyani butunlay o'chiradi. Faqat kirishi kafolatlangan holda bir ipdan sodir bo'ladigan xususiyatlar uchun foydalaning. Bu rejimda lazy minimal qo'shimcha yuk bilan ishlaydi — amalda to'g'ridan-to'g'ri tayinlash kabi.

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

Lazy qachon afzalroq

lazy bir martalik initsializatsiya qilinadigan bog'liqliklar uchun to'g'ri tanlovdir: repozitoriylar, tarmoq mijozlari, keshlar, ma'lumotlar bazalari. Val semantikasi tasodifiy qayta yozishdan himoya qiladi, standart thread-safe esa kodni ko'p ipli muhitda xavfsiz qiladi. lazy shuningdek lateinit bilan mumkin bo'lmagan primitiv turlar bilan to'g'ri ishlaydi.

Android-da lazy tez-tez by viewModels() orqali ViewModel bog'liqliklarini initsializatsiya qilish yoki Retrofit mijozlarini yaratish uchun ishlatiladi. Biroq ehtiyot bo'ling: agar lazy bloki Activity yoki Fragment havolasini tutsa, bu xotira oqishiga olib kelishi mumkin, chunki delegat xususiyatning hayotining oxirigacha closure-ni saqlaydi.

lateinit vs lazy: yondashuvlarni taqqoslash

lateinit va lazy o'rtasidagi tanlov — bu afzal ko'rish masalasi emas, balki xususiyatning tabiati bilan belgilanadigan arxitektura qaroridir. Har bir mexanizm o'z vazifasini hal qiladi va ularning qo'llanish sohalari faqat qisman kesishadi.

Mezonlateinitlazy
Xususiyat turifaqat varfaqat val
Nullabletaqiqlanganruxsat berilgan
Primitiv turlartaqiqlanganruxsat berilgan
Thread-safekafolatlanmaydiSYNCHRONIZED standart
Holatni tekshirish::x.isInitializedtalab qilinmaydi
Xatodagi istisnoUninitializedPropertyAccessExceptioninitsializatsiya blokida xato
Keshlashqo'llanilmaydibir martalik hisoblash
Android BindingView Binding, Data Bindingishlatilmaydi
DI freymvorklariDagger, Hilt, Koinqo'lda injekt qilish

lateinit ishlating, xususiyat initsializatsiyadan keyin o'zgarishi kerak bo'lganda yoki uning yaratilishi tashqi kod tomonidan boshqarilganda. Odatdagi misol — Android Activity-dagi View Binding: binding onCreate-da yaratiladi, lekin var bo'lib qoladi, chunki freymvork bu stsenariy uchun val-ni qo'llab-quvvatlamaydi.

lazy ishlating, xususiyat bir martalik initsializatsiya qilinganda, uni hisoblash qimmat bo'lganda va qiymat ob'ekt hayoti davomida o'zgarmaganda. Klassik misol — repozitoriyga birinchi murojaatda Retrofit mijozining yoki Room ma'lumotlar bazasining dangasa yaratilishi.

lateinit va lazy-ni birlashtirish

Bir sinfda ikkala mexanizm bir vaqtda ishlatilishi mumkin. Masalan, View Binding uchun lateinit va repozitoriy uchun lazy. Bu turli xususiyatlarga turli talablarni aks ettiruvchi oddiy amaliyotdir. Asosiysi — semantikani aralashtirmang: val kerak bo'lgan joyda lateinit ishlatmang va qayta yozilishi kerak bo'lgan xususiyatlar uchun lazy ishlatmang.

lateinit va lazy ishlatishdagi tipik xatolar

lateinit bilan eng keng tarqalgan xato — xususiyat initsializatsiya qilinmasdan oldin unga murojaat qilish. Bu kompilyatsiya bosqichida ushlanmaydigan UninitializedPropertyAccessException-ga olib keladi, chunki Kotlin dasturchiga to'g'ri initsializatsiya ketma-ketligiga ishonadi. Yechim — noaniq vaziyatlarda kirishdan oldin har doim ::property.isInitialized orqali holatni tekshiring.

Ikkinchi keng tarqalgan muammo — semantik jihatdan val bo'lgan xususiyatlar uchun lateinit ishlatish. Agar qiymat bir marta o'rnatilsa va boshqa o'zgarmasa, lazy to'g'ri tanlovdir. U xususiyatni o'zgarmas qiladi, tasodifiy qayta yozishni istisno qiladi va bepul thread-safe qo'shadi.

Uchinchi xato — yon ta'sirga ega lazy. Lazy initsializatsiya bloki tashqi holatni o'zgartirmasligi yoki boshqa lazy xususiyatlarining initsializatsiya tartibiga tayanmasligi kerak, chunki hisoblash ketma-ketligi birinchi murojaatga bog'liq va ravshan bo'lmasligi mumkin. Agar lazy xususiyatlari bir-biriga havola qilsa, bu tsiklik bog'liqlikka va StackOverflowError-ga olib keladi.

To'rtinchi muammo — Android-da lazy orqali xotira oqishi. Agar lazy bloki Activity yoki Fragment havolasini tutsa — delegat closure-ni saqlaydi va axlat yig'uvchi komponentni hatto yo'q qilingandan keyin ham bo'shata olmaydi. Yechim — lazy-ni faqat qisqa muddatli ob'ektlar bilan ishlating yoki Activity o'rniga Application kontekstini uzating.

Beshinchi tipik xato — primitiv turlarga lateinit qo'llash urinishi. Kotlin kompilyatori buni sintaksis darajasida bloklaydi, ammo dasturchilar nullable o'ramlari orqali cheklovni chetlab o'tishga harakat qilishadi. Bu keraksiz null tekshiruvlariga olib keladi va kechiktirilgan initsializatsiyaning afzalliklarini butunlay yo'qotadi.

Tez-tez beriladigan savollar

Kotlin-da lateinit va lazy o'rtasidagi farq nima?

lateinit — konstruktordan keyin initsializatsiyaga ruxsat beruvchi var-xususiyatlar uchun modifikator. lazy — birinchi murojaatda qiymatni hisoblaydigan va uni keshlaydigan val-xususiyatlar uchun delegat. lateinit primitiv turlarni va nullable-ni qo'llab-quvvatlamaydi, lazy esa standart sifatida thread-safe.

lateinit xususiyatining initsializatsiya qilinganligini tekshirish mumkinmi?

Ha, xususiyatga o'rnatilgan havola orqali: ::propertyName.isInitialized. Metod xususiyat allaqachon initsializatsiya qilingan bo'lsa, true qaytaradi. Bu lateinit maydonlari bilan ishlashda UninitializedPropertyAccessException-dan qochishning yagona xavfsiz usuli.

Nega lateinit primitiv turlar bilan ishlatilmaydi?

Primitiv turlar — Int, Double, Boolean va boshqalar — JVM primitivlariga (int, double, boolean) kompilyatsiya qilinadi, ularning «initsializatsiya qilinmagan» holati yo'q. lateinit flag sifatida null ishlatadi, primitivlar esa null bo'la olmaydi, shuning uchun mexanizm bu turlar uchun jismonan amalga oshirib bo'lmaydi.

Lazy uchun standart thread-safe rejimi qaysi?

Standart sifatida LazyThreadSafetyMode.SYNCHRONIZED ishlatiladi — bir nechta iplardan kirishda bir martalik initsializatsiyani kafolatlaydigan ikki marta tekshirish blokirovkasi. Bitta ipli stsenariylar uchun NONE, yuqori raqobat uchun PUBLICATION ishlating.

Android-da lazy o'rniga lateinit qachon ishlatiladi?

Xususiyat initsializatsiyadan keyin o'zgarishi kerak bo'lganda yoki uning yaratilishi freymvork tomonidan boshqarilganda. Odatdagi misol — Android Activity-dagi View Binding: binding onCreate-da yaratiladi va var bo'lishi kerak. Bir martalik initsializatsiya qilinadigan val bog'liqliklari uchun lazy ishlating.

Xulosa

  • lateinit — nullable holda konstruktordan keyin initsializatsiyaga ruxsat beruvchi Kotlin var-xususiyatlari uchun modifikator
  • lazy — bir martalik hisoblash va natijani avtomatik keshlash bilan val uchun xususiyat delegati
  • lateinit initsializatsiyadan oldin kirishda UninitializedPropertyAccessException chiqaradi
  • lazy LazyThreadSafetyMode.SYNCHRONIZED orqali standart sifatida thread-safe
  • lateinit primitiv turlar va nullable xususiyatlar bilan mos kelmaydi
  • lazy Android-da closure-da kontekstni tutganda xotira oqishiga sabab bo'lishi mumkin
  • O'zgaruvchan xususiyatlar uchun lateinit, bir martalik initsializatsiya qilinadigan val bog'liqliklari uchun lazy tanlang

Biz kalit topshirig'i bilan mobil ilovani ishlab chiqamiz

IT Sectr 2017-yildan beri startaplar va korxonalar uchun iOS va Android ilovalarini yaratadi. Biz sizga maslahat beramiz va eng yaxshi yechimni taklif qilamiz.

Loyihani muhokama qilish

Shuningdek o'qing