06 β Null safety¶
β¬ οΈ Oldingi: 05 β To'plamlar: List, Set, Map Β· π README Β· Keyingi: 07 β OOP β obyektga yo'naltirilgan dasturlash β‘οΈ
Bu bobda:
nullnima va nega u dunyodagi eng qimmat xato deb ataladi; Dart'ning sound null safety kuchi β o'zgaruvchi standart holatda null bo'la olmaydi; nullable tip?; xavfsiz ishlash quroli?.,??,??=,!; flow analysis (promotion) β Dartifichida tipni "yangilab" qo'yishi;lateo'zgaruvchi va uning xavfi; va bularning bari Flutter'da nega kamroq xato (crash) keltirib chiqaradi.
null nima va nega u muammo?¶
Tasavvur qiling, sizga quti berishdi. Qutining ustida yorliq bor: "ichida ism bor". Siz qutini ochasiz... lekin u bo'sh. Ichida hech narsa yo'q. Ana shu "hech narsa yo'q" holatini dasturlashda bitta maxsus so'z bilan ataymiz: null.
null β bu "qiymat yo'q" degani. Nol (0) emas, bo'sh satr ('') emas β umuman hech narsa.
Muammo shu yerda boshlanadi. Aytaylik, qutida ism bor deb ishonib, uning uzunligini o'lchamoqchi bo'ldingiz:
String ism = olibKel(); // quti β lekin ichi bo'sh (null) chiqdi
print(ism.length); // BUM! bo'sh qutining uzunligini o'lchab bo'lmaydi
Agar ism aslida null bo'lsa, ism.length chaqirilganda dastur ishlashni to'xtatadi β bu "null reference" xatosi. Foydalanuvchining telefonida ilova birdan yopiladi (crash).
π‘ Bu shunchalik keng tarqalgan va shunchalik ko'p ziyon keltirgan xatoki, uni o'ylab topgan olim Tony Hoare o'zi buni "the billion-dollar mistake" β "milliard dollarlik xato" deb atagan. Yarim asr davomida millionlab dasturlar aynan shu sababdan qulagan.
Eng yomoni: bu xato dastur ishga tushganda, ya'ni foydalanuvchining qo'lida sodir bo'ladi. Siz uni oldindan ko'rmaysiz.
Aynan shu muammoni Dart ildizidan hal qiladi.
Sound null safety β Dart'ning kuchi¶
Dart 3.12'da sound null safety standart va majburiy (uni o'chirib bo'lmaydi). "Sound" so'zi "ishonchli, mustahkam" degani.
Asosiy g'oya juda oddiy va juda kuchli:
Standart holatda hech bir o'zgaruvchi
nullbo'la olmaydi.
Ya'ni oddiy String tipidagi quti doim ichida qiymat saqlashga majbur. Uni bo'sh qoldira olmaysiz:
Bu juda muhim nuqta. Xato sizning telefoningizda emas, balki kodni yozayotgan paytingizda, ekranda qizil chiziq bilan ko'rsatiladi. Kompilyator (kodni tekshiruvchi) sizni oldindan himoya qiladi:
String ism = 'Ali'; // β
to'g'ri β ichida qiymat bor
print(ism.length); // β
xavfsiz β ism hech qachon null bo'lolmaydi, demak crash yo'q
Mana shu β butun bobning yuragi. String tipidagi qutiga ishonsangiz bo'ladi: u hech qachon bo'sh emas. Demak ism.length doim xavfsiz.
π‘οΈ Eski tillarda (yoki eski Dart'da) har bir qiymat yashirin tarzda
nullbo'lishi mumkin edi β siz buni hech qachon bilmasdingiz. Sound null safety bu "yashirin tuzoq"ni butunlay yo'q qiladi.
Nullable tiplar ? β "bo'sh bo'lishi mumkin"¶
Lekin ba'zan bizga aynan "qiymat bo'lmasligi mumkin" holati kerak bo'ladi. Masalan, foydalanuvchi profilida "ikkinchi ism" maydoni bor, lekin uni hamma ham to'ldirmaydi. Bu yerda "qiymat yo'q" β bu normal holat.
Buning uchun tipdan keyin savol belgisi ? qo'yamiz. Bu Dart'ga aytadi: "bu quti bo'sh bo'lishi mumkin (null bo'la oladi)".
String? ikkinchiIsm; // ? bor β null bo'lishi MUMKIN. Boshlang'ich qiymati null.
ikkinchiIsm = 'Vali'; // β
keyin qiymat bersa ham bo'ladi
ikkinchiIsm = null; // β
bo'sh qoldirsa ham bo'ladi β ? shunga ruxsat berdi
? belgisi β bu rozilik (opt-in). Siz Dart'ga "men bu yerda null bo'lishi mumkinligini bilaman va tayyorman" deysiz.
Endi ikki xil tip bor:
| Tip | Misol | null bo'la oladimi? |
|---|---|---|
| non-null (oddiy) | String ism |
Yo'q β β doim qiymat bor |
nullable (? bilan) |
String? ism |
Ha β β bo'sh bo'lishi mumkin |
Va eng qizig'i β nullable qutini to'g'ridan-to'g'ri ishlatib bo'lmaydi:
Dart sizni majburlaydi: agar quti bo'sh bo'lishi mumkin bo'lsa, uni ochishdan oldin tekshir. Bu mantiqan to'g'ri-ku β bo'sh qutining uzunligini o'lchab bo'lmaydi. Endi buni qanday xavfsiz qilishni o'rganamiz.
Nullable bilan xavfsiz ishlash β null quroli¶
Dart bizga nullable qiymatlar bilan ishlash uchun bir nechta qulay operator beradi. Ularni "null quroli" deb atasak bo'ladi.
?. β xavfsiz murojaat (null-aware access)¶
Oddiy nuqta . o'rniga ?. ishlatsangiz, Dart avval "quti bo'shmi?" deb tekshiradi. Agar bo'sh bo'lsa, crash qilish o'rniga shunchaki null qaytaradi:
String? ism; // hozir null
print(ism?.length); // crash YO'Q β natija: null
ism = 'Ali';
print(ism?.length); // natija: 3
?. ni shunday o'qing: "agar bor bo'lsa, uning .length ini ol; agar yo'q bo'lsa β null".
?? β standart qiymat (default)¶
?? operatori shunday ishlaydi: "chap tomon null bo'lsa, o'ng tomonni ol". Bu bizga "zaxira" (fallback) qiymat berishga imkon beradi:
String? ism;
String korsatiladigan = ism ?? 'Mehmon'; // ism null β 'Mehmon' olinadi
print(korsatiladigan); // Mehmon
ism = 'Ali';
print(ism ?? 'Mehmon'); // Ali (chap tomon null emas)
?. va ?? ni birga ishlatish juda keng tarqalgan:
??= β bo'sh bo'lsa o'zlashtir (assign-if-null)¶
??= o'zgaruvchiga qiymat faqat u hozir null bo'lsa beradi. Agar allaqachon qiymat bor bo'lsa β tegmaydi:
String? til;
til ??= 'uz'; // til null edi β endi 'uz'
print(til); // uz
til ??= 'en'; // til allaqachon 'uz', null emas β o'zgarmaydi
print(til); // uz
! β "bang", null assertion (XAVFLI)¶
! operatori Dart'ga shunday deydi: "menga ishon, bu yerda qiymat aniq bor, null emas". U nullable qutini majburan non-null qilib ko'rsatadi:
Lekin agar xato qilsangiz va quti aslida bo'sh bo'lsa β ! darhol crash qiladi:
β οΈ
!β bu xavfsizlik to'rini o'zingiz olib tashlash. U sizning va'dangizga ishonadi; va'dangiz noto'g'ri bo'lsa β dastur qulaydi. Shuning uchun!ni juda kam, faqat 100% ishonchingiz komil bo'lganda ishlating. Ko'p hollarda?.yoki??xavfsizroq tanlov.
To'plamlarda null-aware¶
Bu quroldan to'plamlar (List, Map) bilan ham foydalanamiz:
List<int>? sonlar;
print(sonlar?.length); // null (sonlar bo'sh)
List<int> hammasi = [0, ...?sonlar]; // ...? β sonlar null bo'lsa, hech narsa qo'shilmaydi
print(hammasi); // [0]
...? β bu "spread" operatorining null-aware versiyasi: agar ro'yxat null bo'lsa, xato bermay shunchaki tashlab ketadi.
Flow analysis (promotion) β Dart "biladi"¶
Mana eng go'zal qism. Aytaylik, nullable qutimiz bor va biz uni if bilan tekshirdik:
String? ism = olibKel(); // null bo'lishi mumkin
if (ism != null) {
// shu blok ICHIDA Dart ANIQ biladi: ism null EMAS
print(ism.length); // β
! kerak emas! Dart o'zi tushundi
}
if (ism != null) shartining ichida Dart o'zi mantiqan xulosa qiladi: "bu yerga faqat ism null bo'lmaganda kiriladi, demak ichkarida u aniq qiymatga ega". Shu sabab Dart ism ning tipini vaqtincha String? dan String ga "ko'taradi" (promote). Endi ! ham, ?. ham kerak emas β oddiy . ishlatasiz.
Bu flow analysis (oqim tahlili) yoki type promotion (tip ko'tarilishi) deb ataladi. Dart kodingiz oqimini "o'qib", qaerda nima null emasligini o'zi aniqlaydi.
Blokdan tashqarida esa quti yana eski, nullable holatiga qaytadi:
if (ism != null) {
print(ism.length); // ICHKARIDA: String (xavfsiz)
}
print(ism.length); // β TASHQARIDA: yana String? β xato! null bo'lishi mumkin
Buni "erta qaytish" (early return) bilan ham yozish mumkin va u juda toza chiqadi:
void salomla(String? ism) {
if (ism == null) return; // null bo'lsa β chiqib ketamiz
// shu nuqtadan keyin Dart biladi: ism β String (null emas)
print('Salom, $ism!'); // β
xavfsiz
}
π― Promotion β bu nega Dart kodida
!kam uchrashining sababi. To'g'ri yozilgan kodda Dart sizning o'rningizga deyarli hamma narsani tekshiradi.
late β keyin tayinlanadigan o'zgaruvchi¶
Ba'zan bizda shunday holat bo'ladi: o'zgaruvchi non-null bo'lishi kerak (null bo'lmasligi kerak), lekin uni e'lon qilgan zahoti qiymat berolmaymiz β biroz keyinroq beramiz.
Bu yerda late kalit so'zi yordamga keladi. late Dart'ga shunday va'da beradi: "hozir bo'sh, lekin men buni ishlatishdan oldin albatta to'ldiraman":
late String xabar; // hozir qiymat yo'q, lekin String? ham emas
void tayyorla() {
xabar = 'Salom!'; // keyinroq to'ldiramiz
}
void korsat() {
print(xabar); // β
tayyorla() chaqirilgan bo'lsa β ishlaydi
}
late ning yana bir foydasi β dangasa ishga tushirish (lazy init): qiymat faqat birinchi marta ishlatilganda hisoblanadi. Agar hisoblash "qimmat" (sekin) bo'lsa, bu tejamkor:
Xavfi: agar late o'zgaruvchini to'ldirishdan oldin ishlatib qo'ysangiz, Dart LateInitializationError beradi:
π± Flutter'da nega kerak? Keyinroq (16-bobda)
StatefulWidgetdainitStatedeb nomlangan joyni ko'rasiz β u widget ekranga chiqishidan oldin bir marta ishlaydi. Ko'pincha o'zgaruvchini aynan o'sha yerda to'ldiramiz, e'lon paytida emas. Mana shundalateaynan to'g'ri keladi: "men buniinitStateda to'ldiraman, va'da beraman".
required β qiymat berish shart¶
Funksiyalar bobida (04) nomli parametrlar (named parameters) bilan tanishgansiz. Null safety ular bilan ham bog'liq. Nomli parametr standart holatda ixtiyoriy, demak uni nullable qilish kerak edi. Lekin agar parametr majburiy bo'lishini xohlasangiz, required qo'yasiz:
void ro'yxat({required String ism, int yosh = 0}) {
print('$ism, $yosh yosh');
}
ro'yxat(ism: 'Ali'); // β
ism berildi, yosh standart 0
ro'yxat(yosh: 25); // β XATO β ism berilmadi, lekin required!
required tufayli ism non-null String bo'la oladi: chaqiruvchi unga qiymat berishga majbur, shuning uchun u hech qachon bo'sh qolmaydi. Bu null safety bilan funksiya parametrlari qanday birga ishlashini ko'rsatadi.
Amaliy misollar¶
Endi haqiqiy hayotda tez-tez uchraydigan holatlarni xavfsiz hal qilamiz.
1. Map'dan "bo'lishi mumkin yoki yo'q" qiymat olish¶
Map'dan kalit orqali qiymat olganingizda, natija doim nullable bo'ladi β chunki o'sha kalit umuman bo'lmasligi mumkin:
Map<String, String> sozlamalar = {'til': 'uz'};
String? tema = sozlamalar['tema']; // 'tema' kaliti yo'q β null
String aniqTema = sozlamalar['tema'] ?? 'och'; // ?? bilan zaxira: 'och'
print(aniqTema); // och
2. API'dan kelgan nullable maydon¶
Tarmoqdan (API) kelgan ma'lumotda ba'zi maydonlar bo'lmasligi mumkin. Ularni nullable deb belgilab, xavfsiz ishlatamiz:
class Foydalanuvchi {
final String ism;
final String? telefon; // hamma ham telefon kiritmaydi β nullable
Foydalanuvchi(this.ism, this.telefon);
}
void korsat(Foydalanuvchi u) {
print('Ism: ${u.ism}');
print('Tel: ${u.telefon ?? "ko'rsatilmagan"}'); // null bo'lsa zaxira matn
}
3. Zanjirli xavfsiz murojaat¶
Bir nechta nullable bosqichni ?. bilan zanjirlaymiz β biror joyda null bo'lsa, butun zanjir null qaytaradi:
String? shahar = foydalanuvchi?.manzil?.shahar; // birortasi null bo'lsa β shahar = null
print(shahar ?? 'Manzil yo'q');
Tez-tez uchraydigan xatolar¶
!ni haddan tashqari ishlatish. Har joydaqiymat!yozish β bu xavfsizlik to'rini olib tashlash bilan teng. Ko'p!bor kod β ko'p crash xatari. Avval?.yoki??ni o'ylang.- Kompilyator bilan "urushish". Dart qizil chiziq ko'rsatganda, uni
!bilan "zo'rlab bostirmang". Buning o'rniga tipni?qilib to'g'ri belgilang yokiif (x != null)bilan tekshiring. Kompilyator β dushman emas, yordamchi. lateni to'ldirmaslik.lateβ bu va'da. To'ldirmasdan ishlatsangiz,LateInitializationErrorkeladi. Agar qiymat haqiqatan ham bo'lmasligi mumkin bo'lsa,lateemas,?(nullable) ishlating.- Promotion'ni unutib
!qo'shish.if (x != null)ichidax!yozish β ortiqcha. Dart allaqachon biladi; oddiyxyeting. - Hamma narsani
?qilib qo'yish. Aksincha xato: kerak bo'lmasa-da hamma tipni nullable qilib, keyin har joyda?.va??bilan kurashish. Qiymat doim bo'lishi kerak bo'lsa β uni non-null qoldiring.
Xulosa¶
nullβ "qiymat yo'q". Uni o'ylamay ishlatish β klassik crash sababi ("milliard dollarlik xato").- Sound null safety Dart'da standart: oddiy
Stringhech qachon null bo'la olmaydi. Xato kodni yozish paytida ushlanadi, telefonda emas. ?β tipni nullable qiladi:String? ismbo'sh bo'lishi mumkin.- Null quroli:
?.(xavfsiz murojaat),??(standart qiymat),??=(bo'sh bo'lsa o'zlashtir),!(xavfli tasdiq β kam ishlating). - Promotion:
if (x != null)ichida Dart o'zi tipniString?βStringga ko'taradi. late: non-null, lekin keyin to'ldiriladi (masalan Flutter'dainitState). To'ldirilmasa βLateInitializationError.- Natijada Flutter ilovangiz kamroq qulaydi, va analizator yo'l-yo'lakay sizni to'g'rilab boradi.
Endi siz tiplarni xavfsiz boshqarishni bilasiz. Keyingi bobda bu tiplardan o'zingizning murakkab tiplaringizni β klasslar va obyektlarni quramiz.
Mashqlar¶
1-mashq. Quyidagi koddagi xatoni toping va to'g'rilang. Nega xato berayotganini bir jumlada tushuntiring.
2-mashq. String? sevimliRang o'zgaruvchisi bor. Agar u null bo'lsa, ekranga 'tanlanmagan', aks holda rangning o'zini chiqaring β ?? yordamida bir qatorda yozing.
3-mashq. Quyidagi funksiyani promotion yordamida to'g'rilang (ya'ni ! ishlatmasdan). Funksiya ism null bo'lsa 'Salom, mehmon!', aks holda 'Salom, <ism>!' chiqarsin.
void salomla(String? ism) {
print('Salom, ${ism!}!'); // ism null bo'lsa crash qiladi β tuzating
}
4-mashq. Map<String, int> ballar = {'Ali': 90} lug'ati bor. 'Vali' ning balini xavfsiz oling: agar yo'q bo'lsa, 0 bo'lsin. ?? ishlating va natijani chiqaring.
5-mashq. late int hisob; e'lon qilingan. Quyidagi kodning 2-qatorida nima sodir bo'ladi va nega? Crashni oldini olish uchun kodni tuzating.
6-mashq. ?. va ?? ni birga ishlatib, String? matn ning uzunligini xavfsiz oling: agar matn null bo'lsa, natija 0 bo'lsin. Bir qatorda yozing.
β Yechimlarni ko'rish
1-mashq. Oddiy String non-null, shuning uchun unga null berib bo'lmaydi β bu kompilyatsiya xatosi (kod ishga ham tushmaydi). Ikki yo'l bor:
String laqab = 'mehmon'; // 1-yo'l: haqiqiy qiymat ber
print(laqab);
// yoki bo'sh bo'lishi kerak bo'lsa, tipni nullable qil:
String? laqab2; // 2-yo'l: ? qo'shdik β null bo'lishi mumkin
print(laqab2); // null
2-mashq.
3-mashq. if (ism != null) bilan tekshirsak, Dart ism ni String ga ko'taradi va ! kerak emas:
void salomla(String? ism) {
if (ism == null) {
print('Salom, mehmon!');
} else {
print('Salom, $ism!'); // shu blokda ism β String, ! yo'q
}
}
4-mashq.
Map<String, int> ballar = {'Ali': 90};
int valiBal = ballar['Vali'] ?? 0; // 'Vali' yo'q β null β 0
print(valiBal); // 0
5-mashq. 2-qatorda (print(hisob);) hisob hali to'ldirilmagan, shuning uchun LateInitializationError (crash) sodir bo'ladi β late "keyin to'ldiraman" deb va'da bergan, lekin biz o'qishdan oldin to'ldirmadik. Tuzatish: avval to'ldiramiz, keyin o'qiymiz:
6-mashq.
β¬ οΈ Oldingi: 05 β To'plamlar: List, Set, Map Β· π README Β· Keyingi: 07 β OOP β obyektga yo'naltirilgan dasturlash β‘οΈ