Tarkibga o'tish

08 β€” Dart 3 zamonaviy imkoniyatlari

⬅️ Oldingi: 07 β€” OOP β€” obyektga yo'naltirilgan dasturlash Β· 🏠 README Β· Keyingi: 09 β€” Asinxron Dart ➑️

Bu bobda: Dart 3 ni zamonaviy qiladigan imkoniyatlar bilan tanishasiz β€” records (qiymatlarni klass yozmasdan to'plash), pattern matching va destructuring (ma'lumotni shaklini tanib, bo'lib olish), sealed klasslar (switch ni to'liq β€” exhaustive β€” qiladigan "killer" imkoniyat), class modifiers (base/interface/final/sealed), generics (<T> bilan tip-xavfsiz qayta ishlatish), va exceptions (xatolarni boshqarish). Yo'l-yo'lakay nega Dart makroslari bekor qilinganini va o'rniga nima ishlatilishini tushunamiz.


Nega bu bob muhim

Oldingi boblarda siz Dart'ning "klassik" qismini o'rgandingiz: o'zgaruvchilar, funksiyalar, to'plamlar, null safety, OOP. Bularning hammasi Dart 1 va 2 da ham bor edi. Bu bobda esa aynan Dart 3 olib kelgan yangiliklarni ko'ramiz β€” ular zamonaviy Dart kodini eski darslardan butunlay boshqacha ko'rsatadi.

Nega bularni alohida o'rganamiz? Chunki ular kundalik kodingizni qisqaroq, xavfsizroq va o'qilishi osonroq qiladi. Masalan, sealed klass + switch birikmasi butun bir xatolar turkumini β€” "men bir holatni unutib qoldirdim" degan xatoni β€” kompilyatsiya vaqtida tutib oladi. Bu Flutter'da holat (state) modellashtirishning poydevori; keyinroq Bloc va Riverpod'da aynan shu naqshni ishlatamiz.

Boshlaymiz.


1. Records β€” qiymatlarni klass yozmasdan to'plash

Tasavvur qiling: funksiyadan ikkita qiymat qaytarmoqchisiz β€” masalan ro'yxatdagi eng kichik va eng katta son. Eski yo'l bilan, buning uchun kichkina bir klass yozishingiz kerak edi:

// Eski (zerikarli) yo'l β€” bitta juftlik uchun butun klass
class MinMax {
  final int min;
  final int max;
  MinMax(this.min, this.max);
}

Bu juda ko'p "boilerplate" (takroriy, mazmunsiz) kod. Record aynan shu muammoni hal qiladi: u β€” nomsiz, yengil qiymatlar to'plami. Klass yozmasdan, bir nechta qiymatni bitta "paket"ga jamlaysiz.

Positional (tartibli) record

var pair = (3, 'olma'); // tipi: (int, String)
print(pair.$1); // 3   β€” birinchi maydon
print(pair.$2); // olma β€” ikkinchi maydon

Maydonlarga .$1, .$2, .$3 ... orqali murojaat qilasiz (tartib bo'yicha, 1 dan boshlanadi).

Named (nomli) record

Tartib raqami chalkash bo'lsa, maydonga nom bering:

var odam = (name: 'Ali', age: 25); // tipi: ({String name, int age})
print(odam.name); // Ali
print(odam.age);  // 25

Funksiyadan bir nechta qiymat qaytarish

Records'ning eng kuchli ishlatilishi β€” bu. Endi klass kerak emas:

(int, int) minMax(List<int> sonlar) {
  var min = sonlar.first;
  var max = sonlar.first;
  for (final n in sonlar) {
    if (n < min) min = n;
    if (n > max) max = n;
  }
  return (min, max); // ikkita qiymatni bitta record qilib qaytaramiz
}

void main() {
  var natija = minMax([7, 2, 9, 4]);
  print(natija.$1); // 2  (eng kichik)
  print(natija.$2); // 9  (eng katta)
}

Qiymat bo'yicha tenglik (value equality)

Record'larning maydonlari bir xil bo'lsa, ular teng hisoblanadi β€” siz hech narsa yozmasangiz ham. Klassda bunga == va hashCode ni qo'lda yozishingiz kerak edi:

var a = (1, 'x');
var b = (1, 'x');
print(a == b); // true β€” maydonlar bir xil, demak teng

🧩 Qachon record, qachon klass? Record β€” bir martalik, nomsiz juftlik uchun (funksiyadan qaytariladigan "(min, max)" kabi). Agar to'plamga nom, metod, yoki xulq kerak bo'lsa (masalan User bilan login() metodi) β€” klass yozing. Qoida: agar buni hujjatda "tushuntirish" kerak bo'lsa β€” klass; agar shunchaki "ikki son" bo'lsa β€” record.

Record bitta funksiyadan ikkita qiymatni qaytaradi va ular ikki o'zgaruvchiga ajratiladi β€” klass yozishga hojat yo'q


2. Patterns va destructuring β€” shaklni tanib, bo'lib olish

Pattern (naqsh) β€” bu ma'lumotning shaklini tasvirlaydigan ifoda. Dart shu shaklga qarab qiymatni tekshira oladi va bo'laklarga ajrata (destructure) oladi.

Record'ni ajratish (destructuring)

Yuqoridagi minMax ni eslang. .$1/.$2 o'rniga, natijani to'g'ridan-to'g'ri ikki o'zgaruvchiga ajratib olish mumkin:

var (min, max) = minMax([7, 2, 9, 4]);
print('Eng kichik: $min, eng katta: $max'); // Eng kichik: 2, eng katta: 9

final (a, b) = pair; β€” bu destructuring. O'ng tomondagi record "ochilib", maydonlari chap tomondagi o'zgaruvchilarga tarqatiladi. Bu ancha toza ko'rinadi.

List pattern β€” ro'yxatni ajratish

var royxat = [10, 20, 30];
final [birinchi, _, uchinchi] = royxat; // o'rtadagini e'tiborsiz qoldirdik
print(birinchi);  // 10
print(uchinchi);  // 30

_ (pastki chiziq) β€” "menga bu element kerak emas" degani. Bu wildcard (joker) pattern.

Map pattern β€” lug'atdan kerakli kalitlarni olish

var foydalanuvchi = {'ism': 'Laylo', 'yosh': 22};
final {'ism': ism, 'yosh': yosh} = foydalanuvchi;
print('$ism, $yosh yosh'); // Laylo, 22 yosh

Object pattern β€” obyektni ajratish

Agar sizda Point klassi bo'lsa (x, y getter'lari bilan), uni ham ajratib olishingiz mumkin:

class Point {
  final int x, y;
  Point(this.x, this.y);
}

void main() {
  var p = Point(3, 4);
  final Point(:x, :y) = p; // x va y ni p ichidan tortib oldik
  print('x=$x, y=$y'); // x=3, y=4
}

Point(:x, :y) β€” bu object pattern. :x β€” "x getter'ining qiymatini x nomli o'zgaruvchiga sol" degan qisqacha yozuv.

if-case β€” shartli tanish va ajratish

if-case β€” eng foydali naqshlardan biri. U bir vaqtning o'zida tekshiradi va ajratadi. JSON (tarmoqdan kelgan ma'lumot) bilan ishlashda juda qulay:

Object? json = {'name': 'Dilnoza', 'age': 30};

if (json case {'name': String n}) {
  // Faqat 'name' kaliti bor VA uning qiymati String bo'lsagina bu yerga kiramiz
  print('Ism: $n'); // Ism: Dilnoza
} else {
  print('Noto\'g\'ri format');
}

E'tibor bering: String n β€” bu "qiymat String bo'lsin, va uni n ga sol" degani. Agar tip mos kelmasa, shart bajarilmaydi va else ishlaydi. Bu null safety bilan birga β€” tashqi, ishonchsiz ma'lumotni xavfsiz qabul qilishning eng zamonaviy yo'li.


3. switch + patterns β€” eng kuchli birikma

Siz switch ni 03-bobda ko'rgandingiz. Dart 3 da switch patternlar bilan birga bir necha barobar kuchliroq bo'ldi. Endi qiymatni nafaqat tengligiga, balki shakliga, tipiga, diapazoniga qarab tekshirasiz.

switch ni ifoda (expression) sifatida ham ishlatish mumkin β€” ya'ni qiymat qaytaradi:

String bahola(int ball) => switch (ball) {
  >= 90 => 'A\'lo',         // relational (taqqoslash) pattern
  >= 70 => 'Yaxshi',
  >= 50 => 'Qoniqarli',
  _ => 'Qoniqarsiz',        // _ β€” qolgan barcha holatlar
};

>= 90 β€” bu relational pattern (taqqoslovchi naqsh). Mantiqiy naqshlarni ham birlashtirish mumkin (|| va &&), hamda guard (when) qo'shimcha shart bering:

String tavsifla(int son) => switch (son) {
  0 => 'nol',
  1 || 2 || 3 => 'kichik',                  // logical OR pattern
  int n when n.isEven => 'katta, juft son', // when β€” qo'shimcha shart
  _ => 'katta, toq son',
};

πŸ’‘ switch ifodasi => (arrow) va , (vergul) bilan yoziladi; klassik switch operatori esa case ...: va break bilan. Ifoda β€” natija olmoqchi bo'lganingizda, operator β€” faqat ish bajartirmoqchi bo'lganingizda.


4. Sealed klasslar β€” exhaustive switch ("killer" imkoniyat)

Mana bobning eng muhim qismi.

sealed β€” bu class modifier. U bitta klassdan meros oladigan barcha turlarni bitta faylda "muhrlab" qo'yadi. Tashqaridan unga yangi vorislar qo'shib bo'lmaydi. Bu kompilyatorga juda kuchli ma'lumot beradi: "bu turning barcha mumkin bo'lgan ko'rinishlari mana shular, boshqa yo'q".

Klassik misol β€” geometrik shakllar. Har xil shaklning yuzasini (maydonini) hisoblaymiz:

sealed class Shape {} // muhrlangan β€” vorislar faqat shu faylda

class Circle extends Shape {
  final double r;
  Circle(this.r);
}

class Square extends Shape {
  final double side;
  Square(this.side);
}

class Rectangle extends Shape {
  final double w, h;
  Rectangle(this.w, this.h);
}

Endi yuzani hisoblaymiz. switch ifodasida object patternlar bilan har bir shaklni tanib, ichidagi maydonlarni ajratib olamiz:

double yuza(Shape shape) => switch (shape) {
  Circle(:final r) => 3.14159 * r * r,
  Square(:final side) => side * side,
  Rectangle(:final w, :final h) => w * h,
};

void main() {
  print(yuza(Circle(2)));        // 12.56636
  print(yuza(Square(3)));        // 9.0
  print(yuza(Rectangle(2, 5)));  // 10.0
}

Circle(:final r) β€” "agar bu Circle bo'lsa, uning r maydonini r o'zgaruvchisiga ol" degani.

Nega bu "killer" imkoniyat?

Diqqat: yuqoridagi switch da _ (default) yo'q, lekin kompilyator hech narsadan shikoyat qilmaydi. Nega? Chunki Shape β€” sealed. Kompilyator biladi β€” uchta variant bor (Circle, Square, Rectangle) va siz uchchalasini ham qamrab oldingiz. Bu exhaustive (to'liq) switch deyiladi.

Endi tasavvur qiling, kelajakda yangi shakl qo'shdingiz:

class Triangle extends Shape {
  final double base, height;
  Triangle(this.base, this.height);
}

Shu zahoti yuza funksiyasidagi switch kompilyatsiya xatosi beradi:

The type 'Shape' is not exhaustively matched by the switch cases since it doesn't match 'Triangle(...)'.

Ya'ni kompilyatorning o'zi sizga: "Triangle holatini unutib qoldirding!" deb aytadi β€” ishlaganda emas, kod yozayotganingizda. Bu butun bir xatolar turkumini ("men bir holatni qamrab olmagan ekanman") tubdan yo'q qiladi.

🎯 Flutter'ga ko'prik: Aynan shu naqsh β€” holat (state) modellashtirishning oltin standarti. Masalan, yuklash holatini sealed class YuklashHolati qilib, Yuklanmoqda / Muvaffaqiyat / Xato vorislari bilan modellashtirsangiz, UI'da switch har bir holatni majburan qamrab oladi β€” birortasini unutib bo'lmaydi. Buni keyinroq Bloc va Riverpod boblarida qayta ko'ramiz.

Sealed Shape ierarxiyasi (Circle/Square/Rectangle) switch ifodasiga oziq beradi; kompilyator barcha holatlarni tekshiradi


5. Class modifiers β€” klasslar ustidan nazorat

Dart 3 klass nomidan oldin qo'yiladigan bir nechta modifikator keltirdi. Ular klassingizdan boshqalar qanday foydalanishi mumkinligini boshqaradi. Bu ayniqsa kutubxona (paket) yozayotganda β€” ya'ni API dizaynida β€” muhim.

Asosiy savol uchta: klassni meros olish (extends) mumkinmi? interfeys sifatida amalga oshirish (implements) mumkinmi? nusxa yaratish (new/konstruktor chaqirish) mumkinmi?

Modifikator extends (meros) implements nusxa yaratish Qachon kerak
(modifikatorsiz) βœ… ha βœ… ha βœ… ha Oddiy, ochiq klass
base βœ… ha (majburiy) ❌ yo'q βœ… ha Faqat meros orqali kengaytirilsin
interface ❌ yo'q βœ… ha βœ… ha Faqat shartnoma (kontrakt) sifatida
final ❌ yo'q ❌ yo'q βœ… ha Kengaytirishni butunlay yopish
sealed faqat shu faylda faqat shu faylda ❌ yo'q Exhaustive switch uchun

Qisqacha tushuntiramiz:

  • base β€” vorislar klassni faqat extends qila oladi, implements qila olmaydi. Bu ichki holatingiz har doim meros olinishini kafolatlaydi (har bir voris ota-klass mantiqini "olib ketadi").
base class Hayvon {
  void nafasOl() => print('nafas...');
}
base class Mushuk extends Hayvon {} // βœ… extends β€” to'g'ri
// class Robot implements Hayvon {} // ❌ xato β€” base ni implements qilib bo'lmaydi
  • interface β€” boshqalar uni faqat implements qila oladi (meros emas). Faqat "shartnoma" β€” qanday metodlar bo'lishi kerakligini belgilaydi, lekin amalga oshirishni majburlamaydi.
interface class Tolovchi {
  void tola(double summa) {}
}
class Karta implements Tolovchi {
  @override
  void tola(double summa) => print('$summa to\'landi');
}
  • final β€” klassni butunlay "yopadi": uni na meros olish, na implement qilish mumkin. Kelajakda klassni o'zgartirsangiz, hech kimning kodi buzilmasligini kafolatlaysiz.

  • sealed β€” yuqorida ko'rdik: muhrlangan ierarxiya + exhaustive switch. sealed klassdan to'g'ridan-to'g'ri nusxa yaratib bo'lmaydi (u faqat "umumiy ota").

  • mixin class β€” ham oddiy klass sifatida (extends), ham mixin sifatida (with) ishlatish mumkin bo'lgan ikki tomonlama tur. Bu kodni qayta ishlatishda moslashuvchanlik beradi.

🧱 Maslahat: Boshlovchi sifatida modifikatorlarsiz oddiy klasslardan boshlang. Modifikatorlar β€” kutubxona muallifi uchun nozik asbob: "men foydalanuvchilarga aynan nimaga ruxsat beraman?" degan savolga javob. Ortiqcha qo'llamang.

base/interface/final/sealed modifikatorlarining imkoniyatlar matritsasi: extends, implements va nusxa yaratish bo'yicha taqqoslash


6. Generics β€” <T> bilan tip-xavfsiz qayta ishlatish

Aslida siz generics'ni allaqachon ishlatib kelyapsiz: List<int>, Map<String, int> β€” bulardagi <...> aynan generics. List<int> β€” "faqat int saqlaydigan ro'yxat" degani.

Generic (umumiy tur) β€” bu tipni parametr sifatida qabul qiladigan kod. U bir martda yoziladi, lekin har xil tiplar bilan ishlaydi β€” ham qayta ishlatiladi, ham tip-xavfsiz qoladi.

Generic funksiya

T birinchi<T>(List<T> royxat) => royxat.first;

void main() {
  int x = birinchi([10, 20, 30]);       // T = int  bo'ldi
  String s = birinchi(['a', 'b', 'c']); // T = String bo'ldi
  print('$x, $s'); // 10, a
}

<T> β€” bu "tip joy egasi" (placeholder). Funksiyani chaqirganingizda Dart T ning haqiqiy tipini o'zi aniqlaydi. Natijada birinchi([10,20,30]) aniq int qaytaradi β€” dynamic emas, demak tip xavfsizligi saqlanadi.

Generic klass

class Quti<T> {
  T narsa;
  Quti(this.narsa);

  T ochish() => narsa;
}

void main() {
  var sonQuti = Quti<int>(42);
  var matnQuti = Quti<String>('salom');
  print(sonQuti.ochish());  // 42
  print(matnQuti.ochish()); // salom
}

Bitta Quti klassi β€” ham sonlar, ham matnlar uchun, har biri o'z tipini saqlab.

Bound (chegara) β€” <T extends ...>

Ba'zan generic tipni cheklamoqchi bo'lasiz. Masalan, faqat sonlar bilan ishlovchi funksiya:

T kattasi<T extends num>(T a, T b) => a > b ? a : b;

void main() {
  print(kattasi(3, 7));      // 7   (int β€” num'ning vorisi)
  print(kattasi(2.5, 1.1));  // 2.5 (double β€” num'ning vorisi)
  // kattasi('a', 'b');      // ❌ xato β€” String num emas
}

<T extends num> β€” "T albatta num yoki uning vorisi bo'lsin" degani. Shu tufayli funksiya ichida > operatorini ishlatishimiz mumkin (chunki num da u bor).

πŸ’‘ Nega generics? Ikki sabab: tip xavfsizligi (List<int> ga String qo'sholmaysiz β€” xato kod yozayotganda tutiladi) va qayta ishlatish (bitta Quti<T> ni cheksiz tip uchun ishlatasiz). Ikkalasi birga β€” ortiqcha kod yo'q, lekin xatolar ham yo'q.


7. Exceptions β€” xatolarni boshqarish

Dasturda ko'pincha noto'g'ri narsa yuz beradi: fayl topilmaydi, tarmoq uziladi, foydalanuvchi noto'g'ri qiymat kiritadi. Exception (istisno) β€” bu "bu yerda muammo yuz berdi!" degan signal.

throw β€” xatoni "otish"

double bol(int a, int b) {
  if (b == 0) {
    throw ArgumentError('Nolga bo\'lib bo\'lmaydi'); // xato otdik
  }
  return a / b;
}

throw xatoni "otadi" β€” shu paytdan funksiya to'xtaydi va boshqaruv uni "tutadigan" joyga sakraydi.

try / on / catch / finally β€” xatoni "tutish"

void main() {
  try {
    var natija = bol(10, 0);
    print(natija);
  } on ArgumentError catch (e) {
    // Aniq tipdagi xatoni tutamiz
    print('Argument xatosi: ${e.message}');
  } catch (e) {
    // Boshqa har qanday xato
    print('Kutilmagan xato: $e');
  } finally {
    // BU HAR DOIM ishlaydi β€” xato bo'ldimi, yo'qmi
    print('Tekshiruv tugadi');
  }
}
  • try β€” "xato bo'lishi mumkin" deb gumon qilingan kod.
  • on <Tur> β€” aniq turdagi xatoni tutadi.
  • catch (e) β€” istalgan xatoni tutadi (e β€” xato obyekti).
  • finally β€” natijadan qat'i nazar har doim bajariladigan blok (masalan, faylni yopish, resursni bo'shatish).

O'z exception'ingiz

Hech kim sizni majburlamaydi β€” istalgan Exception klassini o'zingiz yozishingiz mumkin:

class BoshHisobXatosi implements Exception {
  final String sabab;
  BoshHisobXatosi(this.sabab);

  @override
  String toString() => 'BoshHisobXatosi: $sabab';
}

rethrow β€” xatoni qayta uzatish

Ba'zan xatoni qisman qayta ishlab (masalan, jurnalga yozib), keyin yuqoridagi chaqiruvchiga uzatib yubormoqchi bo'lasiz:

void qaytaIshla() {
  try {
    bol(5, 0);
  } catch (e) {
    print('Jurnalga yozildi: $e');
    rethrow; // xatoni o'zgartirmasdan yuqoriga uzatamiz
  }
}

βš–οΈ Qachon throw, qachon null yoki Result? Agar xato β€” kutilgan, oddiy holat bo'lsa (masalan, qidiruvda topilmadi), null qaytaring (06-bobdagi null safety bilan toza ishlaydi). Agar xato β€” istisno, kutilmagan (fayl buzilgan, tarmoq yo'q), throw qiling. Ba'zi loyihalar uchinchi yo'lni tanlaydi: muvaffaqiyat/xatoni bitta sealed "Result" turida modellashtirish β€” bu yuqoridagi sealed naqshning go'zal qo'llanishi.


8. Kodgeneratsiya β€” makroslar haqida haqiqat

Internetdagi eski (yoki noto'g'ri) maqolalarda "Dart makroslari" (macros) haqida o'qigan bo'lishingiz mumkin β€” go'yoki ular freezed kabi paketlarni keraksiz qiladi. Bu endi to'g'ri emas.

⚠️ MUHIM: Dart makroslari 2025-yil yanvarda rasman bekor qilindi (cancelled). Ular tilning qismi emas va kelajakda ham bo'lmaydi. Agar biror manba sizga makroslarni "ishlatishni" o'rgatsa β€” u eskirgan.

Unda data-klasslar va JSON uchun takroriy kodni kim kamaytiradi? Javob β€” build_runner. Bu β€” kod-generatsiya vositasi: u sizning kodingizni o'qib, qo'shimcha Dart faylini avtomatik yozadi. Eng mashhur paketlar:

  • freezed β€” o'zgarmas (immutable) data-klasslar, copyWith, ==, va unionlar (sealed'ga o'xshash holatlar) ni avtomatik yaratadi.
  • json_serializable β€” JSON'dan obyektga va aksincha o'tkazuvchi (fromJson/toJson) kodni avtomatik yozadi.

Ishlatish sodda: maxsus belgilar (annotatsiya) qo'yasiz, keyin terminalda generatsiyani ishga tushirasiz:

dart run build_runner build

Bu sizning freezed/json_serializable annotatsiyalaringizni o'qib, *.g.dart va *.freezed.dart fayllarini yozadi. Bu fayllarni qo'lda yozish kerak emas β€” vosita yozadi. JSON va tarmoq bilan ishlaganda buni 21-bobda amalda ko'ramiz.

Bitta yangi til kalit so'zi β€” augment (augmentations, "to'ldirish") β€” makroslarning tor o'rinbosari: u mavjud e'lonni boshqa faylda "to'ldirish" imkonini beradi va kod-generatorlar o'z natijasini chiroyliroq joylash uchun ishlatadi. Buni makros deb atamang β€” bu boshqa, ancha cheklangan mexanizm; kundalik ishda undan to'g'ridan-to'g'ri foydalanmaysiz.

πŸ’‘ Xulosa: Boilerplate'ni kamaytirish kerakmi? build_runner + freezed/json_serializable. Makroslar β€” yo'q. Shuni eslab qoling, vaqtingizni eskirgan darslarga sarflamaysiz.


Bobning xulosasi

  • Records β€” klass yozmasdan qiymatlarni to'plash; (int, String) positional, ({int x, int y}) named; funksiyadan ko'p qiymat qaytarish uchun ideal; tenglik tekin keladi.
  • Patterns + destructuring β€” ma'lumotni shakliga qarab tanib, bo'laklarga ajratish: final (a, b) = ..., list/map/object patternlar, if-case.
  • switch + patterns β€” taqqoslash (>= 18), mantiqiy (||), guard (when) bilan kuchli moslashtirish; switch ifoda sifatida qiymat qaytaradi.
  • Sealed klasslar β€” exhaustive (to'liq) switch: kompilyator unutilgan holatni yozish vaqtida tutadi. Holat modellashtirishning poydevori.
  • Class modifiers β€” base/interface/final/sealed: API dizaynida klassdan qanday foydalanishni boshqarish.
  • Generics β€” <T> bilan tip-xavfsiz, qayta ishlatiluvchi kod; <T extends num> bilan cheklash.
  • Exceptions β€” throw/try/on/catch/finally/rethrow; o'z exception klasslaringiz; throw vs null tanlovi.
  • Kodgeneratsiya β€” makroslar bekor qilingan; build_runner + freezed/json_serializable ishlating.

Endi siz zamonaviy Dart'ning eng kuchli vositalariga egasiz. Keyingi bobda dasturlashning yana bir muhim qirrasi β€” vaqtni boshqarishni o'rganamiz: Future, async/await va Stream. Bu Flutter'ga o'tishdan oldingi oxirgi Dart bobi.


Mashqlar

1. Record bilan qaytarish. royxatTahlil funksiyasini yozing: List<int> qabul qilib, named record ({int yigindi, double ortacha}) qaytarsin. Keyin natijani destructuring bilan ikki o'zgaruvchiga ajratib chop eting.

2. if-case bilan JSON. Object? data qabul qiladigan salomBer funksiyasini yozing. Agar data β€” {'ism': String} ko'rinishidagi map bo'lsa, "Salom, !" deb chop etsin; aks holda "Noma'lum" desin.

3. Sealed + exhaustive switch. sealed class Tolov yarating, vorislari: Naqd(double summa), Karta(String raqam), Transfer(String hisob). switch ifodasi bilan har bir to'lovni qisqacha matnga aylantiruvchi tasvirla(Tolov t) funksiyasini yozing β€” _ (default) ishlatmang.

4. Generic Stack. Stack<T> generic klassini yozing: push(T), pop() (oxirgisini olib qaytaradi) va boshmi (bo'sh-yo'qligini bool qaytaradi) bilan. Stack<int> da sinab ko'ring.

5. Exception bilan validatsiya. yoshTekshir(int yosh) funksiyasini yozing: agar yosh < 0 bo'lsa, o'zingizning YoshXatosi exception'ini throw qilsin. main ichida try/catch bilan chaqirib, xatoni chiroyli chop eting.

6. (Qiyinroq) Pattern guard. switch ifodasi va when guard'i bilan fasl(int oy) funksiyasini yozing: 12,1,2 β†’ "qish", 3,4,5 β†’ "bahor", 6,7,8 β†’ "yoz", 9,10,11 β†’ "kuz", boshqa β†’ "noto'g'ri oy".

βœ… Yechimlar

1. Record bilan qaytarish:

({int yigindi, double ortacha}) royxatTahlil(List<int> sonlar) {
  var yigindi = sonlar.fold(0, (a, b) => a + b);
  var ortacha = sonlar.isEmpty ? 0.0 : yigindi / sonlar.length;
  return (yigindi: yigindi, ortacha: ortacha);
}

void main() {
  var (:yigindi, :ortacha) = royxatTahlil([10, 20, 30]);
  print('Yig\'indi: $yigindi, o\'rtacha: $ortacha'); // Yig'indi: 60, o'rtacha: 20.0
}

2. if-case bilan JSON:

void salomBer(Object? data) {
  if (data case {'ism': String ism}) {
    print('Salom, $ism!');
  } else {
    print('Noma\'lum');
  }
}

void main() {
  salomBer({'ism': 'Aziza'}); // Salom, Aziza!
  salomBer({'yosh': 20});     // Noma'lum
  salomBer(42);               // Noma'lum
}

3. Sealed + exhaustive switch:

sealed class Tolov {}

class Naqd extends Tolov {
  final double summa;
  Naqd(this.summa);
}

class Karta extends Tolov {
  final String raqam;
  Karta(this.raqam);
}

class Transfer extends Tolov {
  final String hisob;
  Transfer(this.hisob);
}

String tasvirla(Tolov t) => switch (t) {
  Naqd(:final summa) => 'Naqd to\'lov: $summa so\'m',
  Karta(:final raqam) => 'Karta orqali: $raqam',
  Transfer(:final hisob) => 'Transfer hisobga: $hisob',
};

void main() {
  print(tasvirla(Naqd(50000)));        // Naqd to'lov: 50000.0 so'm
  print(tasvirla(Karta('8600****'))); // Karta orqali: 8600****
}

_ yo'q β€” Tolov sealed bo'lgani uchun kompilyator uchchala holatni qamrab olganingizni biladi.

4. Generic Stack:

class Stack<T> {
  final List<T> _ichki = [];

  void push(T element) => _ichki.add(element);

  T pop() => _ichki.removeLast();

  bool get boshmi => _ichki.isEmpty;
}

void main() {
  var s = Stack<int>();
  s.push(1);
  s.push(2);
  print(s.pop());   // 2
  print(s.boshmi);  // false
  print(s.pop());   // 1
  print(s.boshmi);  // true
}

5. Exception bilan validatsiya:

class YoshXatosi implements Exception {
  final String xabar;
  YoshXatosi(this.xabar);
  @override
  String toString() => 'YoshXatosi: $xabar';
}

void yoshTekshir(int yosh) {
  if (yosh < 0) {
    throw YoshXatosi('Yosh manfiy bo\'lishi mumkin emas: $yosh');
  }
  print('Yosh to\'g\'ri: $yosh');
}

void main() {
  try {
    yoshTekshir(25);   // Yosh to'g'ri: 25
    yoshTekshir(-3);   // bu yerda xato otiladi
  } on YoshXatosi catch (e) {
    print('Tutib olindi -> $e'); // Tutib olindi -> YoshXatosi: Yosh manfiy...
  }
}

6. Pattern guard bilan fasl:

String fasl(int oy) => switch (oy) {
  12 || 1 || 2 => 'qish',
  3 || 4 || 5 => 'bahor',
  6 || 7 || 8 => 'yoz',
  9 || 10 || 11 => 'kuz',
  _ => 'noto\'g\'ri oy',
};

void main() {
  print(fasl(1));  // qish
  print(fasl(7));  // yoz
  print(fasl(13)); // noto'g'ri oy
}

Bu yerda when shart emas β€” oddiy || patternlar yetarli. when ni murakkabroq shartda (int n when n > 100) ishlatasiz.


⬅️ Oldingi: 07 β€” OOP β€” obyektga yo'naltirilgan dasturlash Β· 🏠 README Β· Keyingi: 09 β€” Asinxron Dart ➑️