IT rekvalifikace s garancí práce. Seniorní programátoři vydělávají až 160 000 Kč/měsíc a rekvalifikace je prvním krokem. Zjisti, jak na to!
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

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 Inty, 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á Inty, 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 19x (13.41 kB)
Aplikace je včetně zdrojových kódů v jazyce Kotlin

 

Všechny články v sekci
Kolekce v Kotlin
Přeskočit článek
(nedoporučujeme)
Seznam (List) pomocí pole v Kotlin
Článek pro vás napsal Samuel Kodytek
Avatar
Uživatelské hodnocení:
11 hlasů
Autor se věnuje všem jazykům okolo JVM. Rád pomáhá lidem, kteří se zajímají o programování. Věří, že všichni mají šanci se naučit programovat, jen je potřeba prorazit tu bariéru, který se říká lenost.
Aktivity