13. diel - Dynamic, var, null a null aware operátormi
V predchádzajúcom cvičení, Riešené úlohy k 11.-12. lekciu Dart, sme si precvičili získané skúsenosti z predchádzajúcich lekcií.
V minulej lekcii kurzu, Riešené úlohy k 11.-12. lekciu Dart , sme sa naučili pracovať s matematikou.
V tejto lekcii sa zameriame na dátový typ dynamic a hodnotu
null. O tej sme sa už v rámci kurzu neraz zmieňovali a dnes si
ukážeme ako s ňou pracovať.
Dátový typ dynamic
Dátový typ dynamic sa hodí v situáciách, keď neviete
dátový typ premennej, alebo ho nechcete určovať. V premennej tak môže byť
úplne čokoľvek a kedykoľvek sa môže meniť aj jej dátový typ. V jednej
chvíli tak v premennej alfa môže byť celé číslo a potom do
nej priradíme reťazec:
dynamic alfa = 5; print(alfa); alfa = 'aaa'; print(alfa);
S takou premennú sa nám ale veľmi zle pracuje, pretože nevieme my ani
IDE, čo v nej práve je uložené. Tým pádom nám IDE nevie napovedať. Ak to
nie je nevyhnutné alebo vhodné, dynamic by sme používať nemali
a radšej by sme ho mali nahradiť za konkrétny typ údajov.
Var
Spomenieme ešte jedno kľúčové slovo, ktorým je var.
Písať dátové typy je občas zbytočné, napr. V momente, keď vieme, aký
typ údajov dosadzujeme, pretože sme ho napr. Definovali už niekde vyššie.
Predstavme si, že máme napr. 2 premenné, kde obe majú nejaký veľmi
zložitý dátový typ:
List<List<List<List<List<List<int>>>>>> a = []; List<List<List<List<List<List<int>>>>>> b = a;
Písať takéto typy 2x nie je zas až taký problém. Písať je už ale
stále dokola je zbytočné a hlavne neprehľadné. Preto v momente, kedy je
kód dostatočne prehľadný aj pochopiteľný bez opakovania dátového typu,
môžeme využiť kľúčové slovo var, ktoré nám, jednoducho
povedané, dosadí ten správny typ a sprehľadní tým kód.
List<List<List<List<List<List<int>>>>>> a = []; var b = a;
Rovnako tak môžeme kľúčové slovo var použiť kdekoľvek, kde dosadzujeme literál.
var a = 1; // int var b = 1.0; // double var c = 'ahoj'; // String
Pozor však na situácie, kedy by sme chceli mať napr. Zoznam prvkov s
ľubovoľným dátovým typom. Literál by však obsahoval len jeden typ
údajov, čo var vyhodnotí ako zoznam len tohto jedného
dátového typu:
var a = [1, 2, 3, 4, 5]; // List<int> // a.add('alfa'); ... chyba var b = [1, 2, 3, 4, 5, 'alfa']; // List<dynamic> b.add('beta'); var c = <dynamic>[1, 2, 3, 4, 5]; // List<dynamic> c.add('alfa');
V prvom prípade, ak by sme sa pokúsili pridať reťazec do zoznamu, nám
vynadá aj statická analýza kódu, ktorá vypíše chybu "error: The argument
type 'String' can not be assigned to the parameter type 'int'.
(Argument_type_not_assignable at [muj_projekt ] bin \ muj_projekt.dart:
".
Všimnite si, že dátový typ prvkov zoznamu môžeme vynútiť napísaním
typu do špicatých zátvoriek < > pred literál.
Užívajte teda var iba v prípadoch, ak kód nestratí na
prehľadnosti a ak ste si istí, že sa vyhodnotí správne. Var by sa nám
nemal pliesť s dynamic, var za nás len typ sám
napíše, ale neumožňuje ho meniť a premenná sa správa rovnako, ako by sme
typ napísali ručne.
Hodnota null
Niekoľkokrát sme si spomenuli hodnotu null a sľuboval som,
že si ju dovysvětlíme. Čokoľvek v Dart, čo vytvoríme, ale nič do toho
nepriradíte, má predvolenú hodnotu null. Hodnotu
null môžeme chápať ako "nič"; dokonca až také "nič", že na
nej nemôžeme volať takmer žiadne metódy alebo vlastnosti a ak to urobíme,
program sa väčšinou ukončí s chybou.
Pozn .: Na null môžeme v skutočnosti volať iba metódy,
ktoré má Object. Nám zatiaľ postačí vedieť, že je to
napríklad metóda toString(). Tento fakt ale nič nemení na
okolnostiach, že náš program môže spadnúť, čo rozhodne
nechceme.
Že sa nám program naozaj ukončí s chybou si ukážeme na príklade výpise absolútnej hodnoty čísla:
int cislo; // obsahuje null print(cislo.abs());
Výstup programu:
Konzolová aplikácia
Unhandled exception:
NoSuchMethodError: The method 'abs' was called on null.
Receiver: null
Tried calling: abs()
#0 Object._noSuchMethod (dart:core-patch/object_patch.dart:43)
#1 Object.noSuchMethod (dart:core-patch/object_patch.dart:47)
#2 main (file:///D:/git/muj_projekt/bin/muj_projekt.dart:5:15)
#3 _startIsolate.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:265)
#4 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:151)
Už z minula vieme, že hodnota null sa dá využiť aj na
dobré veci (napr. Detekciu, že sme neprevzali naozaj nič, namiesto toho, aby
sme hodnotu nastavili napr. Na 0), ale teraz sme tiež zistili, že
je null potenciálne nebezpečný a musíme s ním zaobchádzať
opatrne.
Najjednoduchšie ochranou, ktorú by ste už mali byť schopní sami zvládnuť, sú podmienky:
int cislo; // obsahuje null if (cislo != null) { print(cislo.abs()); } else { print('Číslo neexistuje.'); }
Výstup programu:
Konzolová aplikácia
Číslo neexistuje.
Asi ale sami tušíte, že písať podmienku vždy, keď si nie sme istí,
že premenná je alebo nie je null nie je moc pekné. A nie je to
ani práve dvakrát prehľadné prehľadné. Našťastie sú v Dart tzv.
Null-aware operátory, ktoré nám s tým veľa pomôžu.
Null-aware operátory
Operátor ??
Operátor ?? použijeme všade tam, kde chceme vrátiť hodnotu
výrazu a v prípadne, že bol výraz null, potom vrátiť jeho
alternatívu.
int cislo; // obsahuje null print(cislo ?? 'Nic.'); cislo = 5; print(cislo ?? 'Nic.');
Výstup programu:
Konzolová aplikácia
Nic.
5
Operátor ?? =
Operátor ??= použijeme všade tam, kde chceme priradiť
alternatívne hodnotu, ak je pôvodná hodnota premennej null.
Výsledok po použití operátora je samozrejme buď pôvodná hodnota, pokiaľ
nebola null, alebo alternatíva.
int cislo; // obsahuje null print(cislo ??= 42); print(cislo); int druheCislo; // obsahuje null druheCislo ??= 666; print(druheCislo);
Výstup programu:
Konzolová aplikácia
42
42
666
Operátor?.
Operátor ?. použijeme všade tam, kde chceme volať metódu a
nie sme si istí, či nie je výraz null. Ak je null,
nič sa nevykoná a výraz vracia hodnota null.
Ukážeme si náš pôvodný príklad s absolútnou hodnotou:
int cislo; // obsahuje null print(cislo?.abs());
Výstup programu:
Konzolová aplikácia
null
Ak by sme chceli volať viac metód za sebou, treba zistiť, či je
absolútna hodnota párna, použijeme operátor ?. v celom výrazu
viackrát.
int cislo; // obsahuje null print(cislo?.abs()?.isEven); cislo = -8; print(cislo?.abs()?.isEven); cislo = 7; print(cislo?.abs()?.isEven);
Výstup programu:
Konzolová aplikácia
null
true
false
Jednotlivé operátormi môžeme samozrejme kombinovať:
int cislo; // obsahuje null print(cislo?.abs() ?? 'Číslo neexistuje.');
Výstup programu:
Konzolová aplikácia
Číslo neexistuje.
Už sme skoro u konca. Budúci lekcie, Ekosystém a konvencie Dart , je vlastne bonusová a nebude sa týkať kódu, ale ekosystému Dart programov a konvenciám v Dart.
