Lekce 1 - Úvod do kolekcí a genericita v Kotlin
Vítejte v online kurzu zaměřeném na různé typy kolekcí programovacího jazyka Kotlin a na genericitu.
Kolekce
Pojem kolekce označuje soubor dat, která jsou většinou stejného typu a slouží ke specifickému účelu. Během Kotlin kurzu jsme se již setkali se dvěma typy kolekcí, bylo to pole a okrajově List. Kolekcí existuje velké množství a ačkoli se zvenku mnohdy tváří podobně, uvnitř fungují velmi odlišně a vybíráme si je podle konkrétního účelu.
Generické a obecné kolekce
Když se zamyslíme nad tím, jak bychom si udělali vlastní kolekci, jistě
bychom po nějaké době dospěli k problému. Byl by jím datový typ kolekce.
Chtěli bychom si např. naprogramovat vlastní List
, vytvořili
bychom třídu MujList.kt
, do ní přidali příslušné metody a
vše potřebné. Protože však chceme, aby byla naše kolekce univerzální a
uměla tedy ukládat např. jak Int
y, tak uživatele, bude problém
s datovým typem prvků uvnitř kolekce. Tento problém vyřeší tzv.
generické kolekce, které se liší od těch obecných.
Obecné kolekce
Jelikož víme, že všechny datové typy mají jako předka třídu
Any
, mohli bychom prvky v naší kolekci ukládat právě do tohoto
datového typu. Do kolekce nyní můžeme uložit v podstatě cokoli. Nevýhodou
je, že sama kolekce skutečný datový typ prvků nezná a proto umí prvky
navracet jen jako obecné objekty. Po získání prvku z kolekce si jej tedy
musíme přetypovat.
Uveďme si příklad, jak by taková obecná kolekce mohla vypadat v Kotlinu,
kdybychom si snažili zkomplikovat život. Použijeme k tomu typ
Array<Any>
:
val array = arrayOf("položka", 123, 30.0) val polozka: String = array[0] as String
Tím, že vytvoříme Array
s různými objekty
(String
, Int
a Double
), se nám vytvoří
Array<Any>
. Abychom tyto položky mohli z pole následně
získat zpět, je třeba je na String
zpětně přetypovat.
Generické kolekce
Generické kolekce řeší problém s datovým typem na úrovni jazyka
Kotlin. Zavádí tzv. genericitu. Zjednodušeně řečeno se jedná o možnost
specifikovat datový typ až ve chvíli vytvoření instance. Ve třídě
samotné kolekce se poté pracuje s generickým typem, který slouží jako
zástupce pro budoucí datový typ. Můžeme si to představit tak, že se
generický typ ve třídě změní např. na String
ve chvíli,
když vytvoříme její instanci. Jedná se tedy o možnost třídy nějakým
způsobem parametrizovat.
Generický Array
již známe a onen datový typ (parametr) se
generickým třídám specifikuje ve špičatých závorkách. Datový typ
můžeme specifikovat pouze jednou, při vytvoření kolekce. Jakékoli další
přetypování odpadá a při čtení získáme vždy objekt daného typu:
val array: Array <String> = arrayOf("položka", "položka_2", "položka_3") val polozka: String = array[0]
Generické kolekce v ostatních programovacích jazycích postupně
vytlačily kolekce obecné. V Kotlinu, jako čistě moderním jazyce, obecné
kolekce již ani neexistují, pokud si nevynutíme přes generiku typ
Any
.
Genericita
Genericita je samozřejmě vlastnost jazyka Kotlin a my ji máme možnost ve svých třídách používat.
Zatím se nebudeme zatěžovat tvorbou vlastní kolekce. Vytvořme si
třídu, která bude jednoduše spravovat jednu proměnnou. Proměnná bude
generická, tedy libovolného datového typu. Založte si nový projekt,
konzolovou aplikaci s názvem Genericita
. Přidejte si novou
třídu, pojmenujme ji nyní pro studijní účely pouze Trida
. V
její deklaraci přidáme generický parametr, který pojmenujeme
T
:
class Trida<T> {
}
Generických parametrů můžeme zadat ve špičatých závorkách více, oddělíme je čárkou. Někdy se to může hodit, my se s tím setkáme dále u generických slovníků.
Přesuneme se do funkce main()
, kde si vytvoříme instanci
naší třídy:
val t = Trida<Int>()
Nezapomeneme na špičaté závorky buď u datového typu, nebo u
konstruktoru. Nyní jsme parametru T
v této instanci třídy
určili datový typ Int
. Stejně tak si můžeme udělat další
instanci té samé třídy a parametru T
dát úplně jiný datový
typ, např. String
. Stačí nám tedy 1 třída pro více datových
typů.
Pokračujme a vytvořme si ve třídě atribut. T
můžeme
použít jako běžný datový typ:
class Trida<T>(private val promenna: T) { }
V main()
aktualizujeme vytvoření instance:
val t = Trida<Int>(10)
Kotlin je dostatečně chytrý, abychom mohli deklaraci typu i vynechat, odvodí si ji sám z typu parametru:
val t = Trida(10) //Typu Trida<Int>
Nyní instance obsahuje atribut promenna
, který je typu
Int
a nabývá hodnoty 10
.
Můžeme dokonce přidat metodu, která bude mít navíc další generický parametr (jiný, než má třída). Mohla by vypadat např. takto:
fun<T2> porovnej(a: T2): Boolean = (a == promenna)
Zkusíme si tedy porovnat náš Int
s nějakým jiným
typem:
t.porovnej<String>("15")
Nebo můžeme zavolat:
t.porovnej("15")
Kotlin obsahuje ještě mocnější konstrukce jako je
například slovíčko out
a in
u definice generik,
ale na úvod do generik mi to přišlo příliš komplikované. Je to spíše
pro pokročilejší publikum a znalce Javy.
Další konstrukce
Pro úplnost si ještě uveďme několik konstrukcí.
Generický parametr třídy je možné blíže specifikovat, přesněji
omezit. Slouží k tomu klíčový operátor :
.
Můžeme tak nastavit, že udaný datový typ musí např. obsahovat rozhraní
Comparable<*>
(k hvězdičce se dostanu níže):
class Trida<T: Comparable<*>>(private val promenna: T) { //... }
Díky tomu můžeme na proměnných typu T
nyní uvnitř třídy
volat metody z daného rozhraní, abstraktní třídy nebo poděděné třídy.
Samotné rozhraní může opět obsahovat generický parametr (zde tomu tak je),
abychom generické typy mohli používat i v hlavičkách jeho metod.
*
Zde slouží jako takový wild card (žolík), chceme
jakoukoli třídu co implementuje Comparable
, ale je nám jedno
jaký typ porovnává. Kdybychom chtěli pouze třídu co porovnává
Int
y, použili bychom:
class Trida<T: Comparable<Int>>(private val promenna: T) { //... }
Nakonec si ukažme, jak můžeme typ parametru omezit z hlediska dědičnosti.
class Trida<A: B, B: C, C> {
}
Výše jsme deklarovali třídu se třemi generickými parametry, kde
A
je potomkem B
a B
je potomkem
C
.
V příští lekci, Seznam (List) pomocí pole v Kotlin, na nás čekají seznamy.
Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.
Stáhnout
Stažením následujícího souboru souhlasíš s licenčními podmínkami
Staženo 20x (13.41 kB)
Aplikace je včetně zdrojových kódů v jazyce Kotlin