Lekce 6 - Typový systém - Null safety v Kotlinu
V předešlém cvičení, Řešené úlohy k 5. lekci Kotlinu, jsme si procvičili nabyté zkušenosti z předchozích lekcí.
Nyní si konečně vysvětlíme koncept zvaný null safety, který je v Kotlinu velmi důležitý, a řekneme si, co znamenají všechny ty vykřičníky ve zdrojovém kódu. S několika jsme se již setkali, a ačkoli se může pro začátek jednat o složitější tematiku, bude lepší, když budeme alespoň tušit, o co se jedná.
Koncept hodnoty null
Programovací jazyky se musí nějak vypořádat se situací, kdy proměnná
nemá žádnou hodnotu. S takovým problémem se často setkáváme u funkcí,
které se nemusí provést korektně. Pokud se např. nepodaří načtení
čísla z konzole, nemělo by být vráceno žádné číslo, ale
„prázdno“. Pokud by nám Kotlin vrátil v tomto případě např. hodnotu
0
nebo -1
, nepoznali bychom, zda se číslo
nepodařilo načíst, nebo zda uživatel vložil právě 0
nebo
-1
. Za tímto účelem se zavedla speciální hodnota
null
, která bezpečně označí, že je proměnná prázdná.
Pokud si v Kotlinu vytvoříme standardní proměnnou, prázdnou hodnotu
null
do ní přiřadit nelze:
// Tento kód je chybný var cislo = 15 cislo = null // Tento řádek vyvolá chybu
Někdo by tam totiž nemusel hodnotu null
očekávat. Abychom ji
mohli do proměnné přiřadit, musíme proměnnou nejdříve deklarovat jako
nullovatelnou.
Nullovatelné typy
Nullovatelný typ můžeme chápat jako jakýsi box, který slouží k
zabalení obyčejné proměnné. Box existuje vždy, avšak po jeho
otevření buď najdeme hodnotu, nebo je prázdný. Nullovatelný typ
vytvoříme tak, že za název datového typu proměnné umístíme
otazník ?
. Zkusme si to:
var moznaCislo: Int? = 15 moznaCislo = null
Kód se již přeložil v pořádku a proměnná moznaCislo
je
nyní prázdná, i když se jedná o číslo. To zní zatím dobře, že?
Nastává však problém, který mnoho ostatních programovacích jazyků
nedokázalo vyřešit. S moznaCislo
by nám nyní nemělo
být umožněno pracovat jako s obyčejnou proměnnou. Podívejme se na
následující kód:
var moznaCislo: Int? = 15 moznaCislo = null println(moznaCislo * 2)
Pokud bychom jej takto napsali a program by se přeložil, mohl by za běhu
spadnout v případě, že by moznaCislo
bylo prázdné. Nemůžeme
přece vynásobit „nezadáno“ dvěma. Když si takový program zkusíme
napsat, zjistíme, že nejde přeložit. Podobně by nám
Kotlin vynadal i při přístupu k vlastnosti nebo metodě nullovatelného typu.
Můžeme si zkusit, že kvůli výpisu délky druhého řetězce nepůjde
následující kód přeložit:
var s1 = "Ahoj" var s2: String? = "Světe" println(s1.length) println(s2.length)
Nejspíš tušíte, že Kotlin nepatří mezi jazyky, které by si tento
problém neohlídaly
Null safety
Mechanismus, který již při překladu kontroluje, jak nullovatelné typy používáme, se nazývá null safety. Existuje několik způsobů, jak nullovatelnou proměnnou použít. Postupně si je vyzkoušíme.
Operátor !!
Začněme tím nejhloupějším, který jsme zatím v kurzu používali, aby
toho na nás nebylo ze začátku moc. Pomocí operátoru !!
můžeme Kotlin degradovat na starší jazyky, jako je např. Java, a kontrolu null safety vypnout. Pokud v
proměnné zrovna nebude null
, vše bude fungovat:
{KOTLIN_CONSOLE} var moznaCislo: Int? = 15 println(moznaCislo!! * 2) {/KOTLIN_CONSOLE}
Zkontroluj, zda výstupy programu odpovídají předloze. S jinými texty testy neprojdou.
Výsledek:
30
Pokud v ní ovšem prázdná hodnota bude, celá aplikace za běhu spadne s chybou:
var moznaCislo: Int? = 15 moznaCislo = null println(moznaCislo!! * 2)
Jelikož bychom při překladu na tuto chybu vůbec nepřišli, nebudeme
řešení s !!
příliš používat.
Podmínka
O něco chytřejší řešení je pracovat s nullovatelnými typy v podmínce
na hodnotu null
. Jelikož se tak vyhneme pádu programu, Kotlin
nám program dovolí přeložit:
{KOTLIN_CONSOLE} var moznaCislo: Int? = 15 if (moznaCislo != null) println(moznaCislo * 2) else println("Číslo není zadané!") {/KOTLIN_CONSOLE}
Zkontroluj, zda výstupy programu odpovídají předloze. S jinými texty testy neprojdou.
Bezpečné volání
Určitě tušíte, že existuje lepší řešení než stále psát podmínky
na null
. Pomocí operátoru ?.
(otazník a tečka) se
buď přistoupí k dané vlastnosti, nebo se vrátí null
v
případě, že je proměnná prázdná.
?.let
Pokud bychom použili bezpečné volání spolu s klíčovým slovem
let
, spustil by se kód ve složených závorkách pouze v
případě, že by v proměnné byla nenullová hodnota:
{KOTLIN_CONSOLE} var moznaCislo: Int? = 15 moznaCislo?.let { println(it) } {/KOTLIN_CONSOLE}
Zkontroluj, zda výstupy programu odpovídají předloze. S jinými texty testy neprojdou.
Klíčové slovo it
následně v bloku obsahuje tuto hodnotu.
Pokud by bylo moznaCislo
null
, program by se
přeložil a výpis by se nespustil.
Řetězení ?.
Tuto funkcionalitu využijeme pouze v případě, kdy se chceme zeptat přes řetěz vlastností, jako např.:
zak?.ucitel?.nadrizeny?.jmeno
Výraz výše vrátí buď název ředitele školy (nadřízeného učitele
žáka), nebo null
v případě, že je jakýkoli článek výrazu
prázdný. Vyhneme se tak spoustě podmínek, musíme však pamatovat na to, že
ve výsledku máme sice jeden, ale stále nullovatelný typ.
Takto bychom mohli i bezpečně přiřadit, již bez potřeby dalších podmínek:
zak?.ucitel?.nadrizeny?.jmeno = "Seymour Skinner"
Elvis operátor

Jak vznikl název tohoto operátoru, asi není třeba vysvětlovat Elvise používáme spolu s
operátorem
?.
a umožňuje nám zeptat se, zda je hodnota v
nullovatelné proměnné null
, a případně použít jinou
výchozí hodnotu. Opět si to zkusme na našem příkladu:
{KOTLIN_CONSOLE} var moznaText: String? = "Ahoj světe" println(moznaText?.length ?: 0) {/KOTLIN_CONSOLE}
Zkontroluj, zda výstupy programu odpovídají předloze. S jinými texty testy neprojdou.
Na pravé straně Elvis operátoru můžeme použít i return
nebo vyvolání výjimek (viz další kurzy).
V každém ze způsobů práce s hodnotou null
platí, že pokud je hodnota null
, další výrazy (metody)
určené pro případ, kdyby hodnota null
nebyla, se nespustí.
V příští lekci, Pole v Kotlinu, se budeme věnovat polím.