Lekce 1 - Úvod do kolekcí a genericita v Javě
V dnešní lekci si řekneme úvodní teorii ke kolekcím v Javě, které si v tomto kurzu podrobněji rozebereme.
Kolekce
Pojem kolekce označuje soubor dat, které jsou většinou stejného typu a
slouží ke specifickému účelu. Během kurzů jsme se již setkali se dvěma
typy kolekcí, bylo to pole a ArrayList
. 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. Java disponuje velkým
množstvím předpřipravených kolekcí, se kterými se v této sekci postupně
seznámíme a zkusíme si s nimi pracovat.
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í ArrayList
,
vytvořili bychom třídu MujList.java
, 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. Existují 2
varianty, jak tento problém vyřešit a i samotná Java obsahuje kolekce
těchto 2 typů.
Obecné kolekce
Jelikož víme, že všechny datové typy mají jako předka třídu Object, můžeme 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 kolekce, u které neuvedeme generický typ:
ArrayList list = new ArrayList(); list.add("položka"); String polozka = (String)list.get(0);
Po vytvoření listu si do něj přidáme položku typu String
.
Abychom tuto položky mohli z listu získat zpět, je třeba ji na
String
zpětně přetypovat.
Pro funkčnost kódu musíme přidat
import java.util.ArrayList;
.
Generické kolekce
Generické kolekce řeší problém s datovým typem na úrovni jazyka Java.
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ý ArrayList
již známe a 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á:
ArrayList<String> list = new ArrayList<String>(); list.add("položka"); String polozka = list.get(0);
Program funguje úplně stejně, jako ten s negenerickou kolekcí
ArrayList
, nicméně číst můžeme bez nepohodlného
přetypování.
Generické kolekce nahradily kolekce obecné a ty se již příliš nepoužívají. V kurzu se budeme věnovat generickým kolekcím a jejich negenerické verze pouze zmíníme.
Genericita
Genericita je samozřejmě vlastnost jazyka Java 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
:
public 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 map.
Přesuneme se do metody main()
, kde si vytvoříme instanci
naší třídy:
Trida<Integer> instance = new Trida<>();
Nezapomeneme na špičaté závorky jak u datového typu, tak u konstruktoru.
Od Javy 7 není potřeba přidávat datový typ u konstruktoru. Nyní jsme
parametru T
v této instanci třídy určili datový typ
Integer
. 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:
private T promenna;
Třídě ještě dodáme konstruktor, který proměnnou inicializuje:
public Trida(T promenna) { this.promenna = promenna; }
V main()
aktualizujeme vytvoření instance:
Trida<Integer> instance = new Trida<>(10);
Nyní instance obsahuje atribut promenna
, který je typu
Integer
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:
public <T2> boolean porovnej(T2 a) { return promenna.equals(a); }
Zkusíme si tedy porovnat náš Integer
s nějakým jiným
typem:
instance.<String>porovnej("15");
Generický typ si Java dokáže odvodit z parametru, takže ho není potřeba za názvem funkce uvádět.
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é slovo extends
. Můžeme tak
nastavit, že udaný datový typ musí např. implementovat rozhraní
Comparable
:
public class Trida<T extends Comparable> { ... }
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.
Nakonec si ukažme, jak můžeme typ parametru omezit z hlediska dědičnosti.
public class Trida<A extends B, B extends 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, Java Collections Framework, se podíváme na List
y,
představíme si různé implementace této kolekce a jejich výhody a
nevýhody.
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 593x (2.58 kB)
Aplikace je včetně zdrojových kódů v jazyce Java