Vydělávej až 160.000 Kč měsíčně! Akreditované rekvalifikační kurzy s garancí práce od 0 Kč. Více informací.
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 ve Swift

Vítejte v online kurzu zaměřeném na různé typy kolekcí programovacího jazyka Swift.

Kolekce

Pojem kolekce označuje soubor dat, které jsou většinou stejného typu a slouží ke specifickému účelu. Během předchozích Swift kurzů jsme se již setkali s polem (Array) jako základní kolekcí. Swift výběr kolekcí velmi zjednodušuje, protože nabízí pouze hlavní tři. Základní pole již známe, později si ukážeme pokročilejší metody a seznámíme se se zbylými kolekcemi Dictionary (slovník) a Set (množina).

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í Array, vytvořili bychom třídu MojeArray.swift, 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. Existují 2 varianty, jak tento problém vyřešit, prakticky se ale používá pouze jedna. Ta lepší samozřejmě :-)

Obecné kolekce

Ve Swiftu můžeme libovolný datový typ přiřadit do typu Any. Ten bychom v obecné kolekci mohli použít a do kolekce poté 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 bychom museli řešit přetypování.

Generické kolekce

Generické kolekce řeší problém s datovým typem na úrovni jazyka Swift. 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. Onen datový typ (parametr) se generickým třídám specifikuje ve špičatých závorkách. Máme možnost specifikovat datový typ pouze jednou, při vytvoření kolekce. Jakékoli další přetypování odpadá. Ačkoli jsme používali k vytvoření polí zjednodušenou syntaxi, bylo by jej možné vytvořit i takto:

var pole = Array<String>()
pole.append("položka")

let polozka = pole[0]

Zápis [String](), který jsme byli zvyklí používat doposud, by fungoval úplně stejně.

Genericita

Genericita je samozřejmě vlastnost jazyka Swift 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 souboru main.swift, kde si vytvoříme instanci naší třídy:

let instance = Trida<Int>()

Nezapomeneme na špičaté závorky jak u datového typu, tak 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ě vlastnost. T můžeme použít jako běžný datový typ:

private var promenna: T

Třídě ještě dodáme konstruktor, který proměnnou inicializuje.

init(promenna: T) {
    self.promenna = promenna
}

V main.swift aktualizujeme vytvoření instance:

let instance = Trida<Int>(promenna: 10)

Nyní instance obsahuje vlastnost 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:

func porovnej<T2>(a: T2) -> Bool {
    return type(of: promenna) == type(of: a)
}

Metoda porovnává datový typ vlastnosti promenna s datovým typem parametru. Mohla by s proměnnými ale samozřejmě dělat cokoli jiného. Zkusíme si tedy porovnat náš Int s nějakým jiným typem:

instance.porovnej(a: "text")

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 dvojtečka přímo za generickým parametrem. Můžeme tak nastavit, že udaný datový typ musí např. obsahovat protokol Equatable (aby fungoval == operátor):

class Trida<T: Equatable> {
    // ...
}

Díky tomu můžeme na proměnných typu T nyní uvnitř třídy volat metody z daného rozhraní. Samotné rozhraní může opět obsahovat generický parametr, abychom generické typy mohli používat i v hlavičkách jeho metod.

Můžeme také použít zápis pomocí klíčové slova where:

class Trida<T> where T:Equatable {
    // ...
}

Záleží, co se vám líbí více. Pro přehlednost bych doporučil where používat, když máte více specifikací.

Pokud chceme specifikovat více protokolů, tak je v první variantě zápisu oddělíme pomocí &. Při where stačí použít čárku a zopakovat pro jaký parametr specifikaci uvádíme.

class Trida<T: Equatable & Comparable> {
    // ...
}
class Trida<T> where T: Equatable, T: Comparable {
    // ...
}

V příští lekci, Filtrování a mapování polí ve Swift, se znovu podíváme na pole a vysvětlíme si pokročilé metody. Naučíte se pole řadit a filtrovat podle vlastních parametrů.


 

Všechny články v sekci
Kolekce ve Swift
Přeskočit článek
(nedoporučujeme)
Filtrování a mapování polí ve Swift
Článek pro vás napsal Filip Němeček
Avatar
Uživatelské hodnocení:
5 hlasů
Autor se věnuje vývoji iOS aplikací (občas macOS)
Aktivity