6. diel - Typový systém: Null safety v Kotlin
V predchádzajúcom cvičení, Riešené úlohy k 5. lekcii Kotlin, sme si precvičili získané skúsenosti z predchádzajúcich lekcií.
V minulej lekcii, Riešené úlohy k 5. lekcii Kotlin , sme sa naučili ušetriť si prácu pomocou cyklov. Teraz si konečne vysvetlíme koncept tzv. Null safety, ktorý je v Kotlin veľmi dôležitý a povieme si, čo znamenajú všetky tie výkričníky v zdrojovom kóde. S niekoľkými sme sa už stretli a hoci sa môže pre začiatok jednať o zložitejší koncept, tak bude lepšie, keď budeme aspoň tušiť, o čo sa jedná.
Koncept hodnoty null
Programovacie jazyky sa musí nejako vysporiadať so situáciou, kedy
premenná nemá žiadnu hodnotu. S takýmto problémom sa často stretávame u
funkcií, ktoré sa nemusia vykonať korektne. Ak sa napr. Nepodarí načítanie
čísla z konzoly, nemalo by byť vrátený žiadne číslo, ale "prázdno". Ak
by nám Kotlin vrátil v tomto prípade napr. Hodnotu 0
alebo
-1
, nepoznali by sme či sa číslo nepodarilo načítať alebo či
používateľ vložil práve 0
alebo -1
. Za týmto
účelom sa vymyslela špeciálnu hodnotu null
, ktorá bezpečne
označí, že je premenná prázdna.
Pokiaľ si v Kotlin vytvoríme štandardnej premennú, prázdnu hodnotu
null
do nej priradiť nedá:
// Tento kód je nesprávny var cislo = 15 cislo = null // Tento riadok vyvolá chybu
Niekto by ju tam totiž nemusel očakávať. Aby sme null
mohli
do premennej priradiť, musíme premennú najprv deklarovať ako
nullovatelnou.
Nullovatelné typy
Nullovatelný typ môžeme chápať ako akýsi box, ktorý slúži na
zabalenie obyčajné premenné. Box vždy existuje, po jeho otvorení
ale hodnotu buď nájdeme, alebo je prázdny. Nullable typ vytvoríme
tak, že za názov dátového typu premennej umiestnime
otáznik ?
. Skúsme si to:
var moznaCislo: Int? = 15 moznaCislo = null
Kód sa už preložil v poriadku a premenná moznaCislo
je teraz
prázdna, aj keď sa jedná o číslo. To znie zatiaľ dobre, že?
Je tu však problém, ktorý mnoho ostatných programovacích jazykov
nedokázalo vyriešiť. S moznaCislo
by nám teraz nemalo
byť umožnené pracovať ako s obyčajnou premennou. Ak by sme
napísali:
var moznaCislo: Int? = 15 moznaCislo = null println(moznaCislo * 2)
a program sa preložil, mohol by za behu spadnúť v prípade, že by
moznaCislo
bolo prázdne. Nemôžeme predsa vynásobiť "nezadané"
dvoma. Keď si taký program skúsite napísať, zistíte, že nejde
preložiť. Podobne by nám Kotlin vynadal aj pri prístupe k
vlastnosti alebo metóde nullovatelného typu. Môžete si skúsiť, že kvôli
výpisu dĺžky druhého reťazca nepôjde nasledujúci kód preložiť:
var s1 = "Ahoj" var s2: String? = "Svete" println(s1.length) println(s2.length)
Asi ste tušili, že Kotlin nepatrí medzi jazyky, ktoré by si tento problém neohlídaly
Null safety
Mechanizmus, ktorý už pri preklade kontroluje ako nullovatelné typy používame, sa nazýva null safety. Existuje niekoľko spôsobov ako nullovatelnou premennú použiť. Postupne si ich vyskúšame.
Operátor !!
Začnime tým najhlúpejšom, ktorý sme zatiaľ v kurze používali, aby
toho na nás nebolo zo začiatku moc. Pomocou operátora !!
môžeme Kotlin degradovať na staršie jazyky ako je napr. Java a kontrolu null safety vypnúť. Ak v
premennej zrovna nebude null
, všetko bude fungovať:
{KOTLIN_CONSOLE}
var moznaCislo: Int? = 15
println(moznaCislo!! * 2)
{/KOTLIN_CONSOLE}
výsledok:
30
Ak v nej však prázdna hodnota bude, celá aplikácia za behu upadne s chybou:
var moznaCislo: Int? = 15 moznaCislo = null println(moznaCislo!! * 2)
Keďže pri preklade by sme na túto chybu vôbec neprišli, nebudeme
riešenie s !!
príliš používať.
Podmienka
O niečo múdrejší riešenia je pracovať s nullovatelnými typmi v
podmienke na hodnotu null
. Keďže sa tak vyhneme pádu programu,
Kotlin nám program dovolí preložiť:
{KOTLIN_CONSOLE}
var moznaCislo: Int? = 15
if (moznaCislo != null)
println(moznaCislo * 2)
else
println("Číslo nie je zadané!")
{/KOTLIN_CONSOLE}
Bezpečné volanie
Určite tušíte, že existuje lepšie riešenie ako písať stále podmienky
na null
. Pomocou operátora ?.
(otáznik bodka) sa
buď pristúpi k danej vlastnosti alebo sa vráti null
v prípade,
že je premenná prázdna.
?.let
Ak by sme použili bezpečné volania spolu s kľúčovým slovom
let
, spustil by sa kód v zložených zátvorkách iba v prípade,
že by v premennej bola ne-nullová hodnota:
{KOTLIN_CONSOLE}
var moznaCislo: Int? = 15
moznaCislo?.let { println(it) }
{/KOTLIN_CONSOLE}
Kľúčové slovo it
následne v bloku obsahuje túto hodnotu.
Pokiaľ by bolo moznaCislo
null
, program by sa
preložil a výpis by sa nespustil.
Reťazenie ?.
Túto funkcionalitu využijeme len v prípade, keď sa chceme opýtať cez reťaz vlastností ako napr .:
zak?.ucitel?.nadrizeny?.jmeno
Výraz vyššie vráti buď názov riaditeľa školy (nadriadeného učiteľa
žiaka) alebo null
v prípade, že je akýkoľvek článok výrazu
prázdny. Ušetríme tak veľa podmienok, avšak musíme pamätať na to, že vo
výsledku máme stále hoci jeden tak stále nullovatelný typ.
Mohli by sme takto aj bezpečne priradiť, už bez potreby ďalších podmienok:
zak?.ucitel?.nadrizeny?.jmeno = "Seymour Skinner"
Elvis operátor
Ako vznikol názov tohto operátora asi netreba vysvetľovať Elvisa používame spolu s
operátorom ?.
a umožňuje nám opýtať sa, či je hodnota v
nullovatelné premennej null
a prípadne použiť inú predvolenú
hodnotu. Opäť si to skúsme na našom príklade:
{KOTLIN_CONSOLE}
var moznaText: String? = "Ahoj svete"
println(moznaText?.length ?: 0)
{/KOTLIN_CONSOLE}
Na pravej strane Elvis operátora môžeme použiť aj return
alebo vyvolanie výnimiek (pozri ďalšie kurzy).
V každom zo spôsobov práce s hodnotou null
platí, že ak je hodnota null
, ďalšie výrazy (metódy) určené
pre prípad keby null
nebola sa nespustí.
V budúcej lekcii, Poľa v Kotlin , sa budeme venovať poliam.