Generics
Jeneriklar yoki generiklar sizga dasturga moslashuvchanlikni qo'shish va muayyan turlarga qattiq bog'lanishdan xalos bo'lish imkonini beradi. Ba'zan funksionallikni har qanday turdagi ma'lumotlardan foydalanishi mumkin bo'lgan tarzda aniqlash kerak bo'ladi.
Misol uchun, biz identifikatorni (foydalanuvchi identifikatori) o'z ichiga olgan foydalanuvchi sinfini aniqlaymiz:
1
2
3
4
5
|
class Person{
int id; // идентификатор пользователя
String name; // имя пользователя
Person(this.id, this.name);
}
|
Bunday holda, id raqamli qiymat sifatida o'rnatiladi, ya'ni u 2, 5, 7 va boshqalarga teng bo'lishi mumkin. Ammo keyinchalik id turi sifatida strings yoki boshqa sinflardan foydalanishni xohlashimiz mumkin. Moslashuvchanlikni qo'shish uchun biz turga qattiq bog'lanib qolmaslik uchun dinamik operatordan foydalanishimiz mumkin:
1
2
3
4
5
6
7
8
9
10
11
12
|
class Person{
dynamic id; // идентификатор пользователя
String name; // имя пользователя
Person(this.id, this.name);
}
void main (){
Person tom = Person(134, "Tom");
print(tom.id);
Person bob = Person("324", "Bob");
print(bob.id);
}
|
Biroq, id maydoni qaysi ob'ektni ifodalashini bilmasligimiz mumkin, ayniqsa Person klassi uchinchi shaxslar tomonidan yozilgan tashqi kutubxonada aniqlangan bo'lsa. Va bu holda raqamni olishga harakat qilganda, biz istisnoga duch kelamiz:
1
2
|
Person bob = Person("324", "Bob");
int id = bob.id; // Ошибка
|
Har xil turlar bilan ishlash uchun ikki turdagi Shaxsni taqdim etishingiz mumkin:
1
2
3
4
5
6
7
8
9
10
|
class PersonInt{
int id;
String name;
PersonInt(this.id, this.name);
}
class PersonString{
string id;
String name;
PersonString(this.id, this.name);
}
|
Ammo bu holda biz boshqa muammoga duch kelamiz - kodni takrorlash.
Jeneriklar yoki generiklar ko'proq turdagi xavfsizlikni ta'minlaydi va kodning takrorlanishini oldini olishga yordam beradi. Keling, generiklar yordamida Person sinfining kodini qayta yozamiz:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
void main (){
Person bob = Person("324", "Bob");
print(bob.id.runtimeType); // String
Person sam = Person(123, "Sam");
print(sam.id.runtimeType); // int
}
class Person{
T id; // идентификатор пользователя
String name; // имя пользователя
Person(this.id, this.name);
}
|
Biz ifodasidan Person sinfining ma'lum bir T turi bilan terilganligini ko'rsatish uchun foydalanamiz. T umumiy parametr deb ham ataladi. Bundan tashqari, parametr nomi o'zboshimchalik bilan bo'lishi mumkin, lekin odatda katta harflar ishlatiladi, ko'pincha T harfi. Shundan so'ng biz T ni oddiy tip sifatida ishlatishimiz mumkin, masalan, ushbu turdagi o'zgaruvchilarni aniqlang: T id ;.
Dasturni bajarishda T o'rniga ma'lum bir tur almashtiriladi. Bundan tashqari, tur o'tkazilgan qiymatlar asosida dinamik ravishda hisoblab chiqiladi. RuntimeType maydonidan foydalanib, biz o'zgaruvchining maxsus ma'lumotlar turini olishimiz mumkin. Ob'ektlarda qanday turlar ishlatilishini ham aniq belgilashimiz mumkin:
1
2
3
4
|
Person bob = Person("324", "Bob");
print(bob.id.runtimeType);
Person sam = Person(123, "Sam");
print(sam.id.runtimeType);
|
Sinf nomidan keyin burchakli qavs ichida sinf yoziladigan tur ko'rsatiladi (Shaxs ).
Xuddi shunday, biz umumiy usullar va funktsiyalarni belgilashimiz mumkin. Masalan, kichik ro'yxatga olish funktsiyasini aniqlaymiz va ishlatamiz:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
void main (){
int x = 20;
log(x);
x = 34;
log(x);
String name = "Tom";
log(name);
}
void log(T a){
// DateTime.now() - получает текущую дату и время
print("${DateTime.now()} a=$a");
}
|
Umumiy usulni yaratish uchun umumiy parametr nomi uning nomidan keyin burchakli qavs ichida ko'rsatiladi. Shundan so'ng, buning ichida umumiy T turi usul ichida - parametrlar yoki o'zgaruvchilar turi sifatida ishlatilishi mumkin.
Jeneriklarning cheklovlari
Ba'zan siz generiklardan foydalanishingiz kerak bo'ladi, lekin siz T parametri o'rniga funktsiya yoki sinfdagi har qanday turni qabul qilishni xohlamaysiz. Masalan, bizda bank hisobini ifodalovchi quyidagi Hisob sinfi mavjud:
1
2
3
4
5
6
|
class Account{
int id; // номер счета
int sum; // сумма на счете
Account(this.id, this.sum);
}
|
Pul mablag'larini bir hisobdan ikkinchisiga o'tkazish uchun biz barcha operatsiyalarni bajarish uchun Hisob sinfi ob'ektlaridan foydalanadigan Transaction sinfini aniqlashimiz mumkin.
Ammo Hisob sinfi ko'plab avlodlarga ega bo'lishi mumkin: DepositAccount (depozit hisobi), DemandAccount (talab hisobi) va boshqalar. Tranzaksiya sinfida qaysi hisob turlari ishlatilishini bila olmaymiz. Tranzaktsiyalar faqat talab hisoblari o'rtasida amalga oshirilishi mumkin. Va bu holda, Hisob turi universal parametr sifatida o'rnatilishi mumkin:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
class Transaction{
T fromAccount; // с какого счета перевод
T toAccount; // на какой счет перевод
int sum; // сумма перевода
Transaction(this.fromAccount, this.toAccount, this.sum);
void execute(){
if (fromAccount.sum > sum){
fromAccount.sum -= sum;
toAccount.sum += sum;
print("Счет ${fromAccount.id}: ${fromAccount.sum}\$ \nСчет ${toAccount.id}: ${toAccount.sum}\$");
}
else
{
print("Недостаточно денег на счете ${fromAccount.id}");
}
}
}
|
iborasidan foydalanib, biz foydalaniladigan T turi, albatta, Hisob sinfi yoki uning merosxo'ri bo'lishi kerakligini bildiramiz. Ushbu cheklov tufayli biz Transaction sinfidagi barcha T tipidagi ob'ektlardan aynan Account ob'ektlari sifatida foydalanishimiz va shunga mos ravishda ularning maydonlari va usullariga kirishimiz mumkin.
Endi Tranzaksiya sinfini qo'llaymiz:
1
2
3
4
5
6
7
|
void main (){
Account acc1 = Account(1857, 4500); // sum = 4500;
Account acc2 = Account(3453, 5000); // sum = 5000;
Transaction transaction = Transaction(acc1, acc2, 1900);
transaction.execute();
}
|
Konsol chiqishi:
Счет 1857: 2600$
Счет 3453: 6900$
Do'stlaringiz bilan baham: |