Generické a parametrizované datové typy a metody Rudolf PECINOVSKÝ [email protected] 1
Vaše jistota na trhu IT
Úvod ►Shrnutí či téma
2
Doporučená literatura ► Java 5.0 – Novinky jazyka a upgrade aplikaci ► Vydala Computer Press, ISBN 80-251-0615-2, EAN: 978-80-2510-615-0 ► Probírá vlastnosti, které stále mnozí neznají – především parametrizované typy a anotace ► Lze volně stáhnout na adrese http://knihy.pecinovsky.cz/ java5novinky/
Terminologie a zkratky ► Používané zkratky: ● GT / PT – generický / parametrizovaný typ (typy) ● GM / PM – generický / parametrizovaná metoda (metody) ● GTM / PTM – generické / parametrizované typy a metody ● TP – typový parametr ► Generický = obecný, obecně použitelný, rodový ● Parametrizovaná metoda != metoda s parametry ► GT/GM mají typové parametry zastupující některé z typů použitých v definovaném typu, resp. metodě ► Seznam typových parametrů se uvádí ve špičatých závorkách ► Parametrizovaný typ je potomek generického typu s dosazenou hodnotou typového parametru
● G: ● P:
class Typ1G<E>{} interface Typ2G {} class Typ1P extends Typ1G<String> {} interface Typ2P extends Typ2G<String, Object> {} 5
Řešený problém ► Při definici kontejnerů nebylo možno specifikovat typ hodnot ukládaných do kontejneru – typovou kontrolu bylo možno provést až za běhu ► Některé jazyky (např. Ada, C++) řeší tento problém zavedením typových parametrů, jejichž hodnoty je možno kontrolovat již při překladu ► Cíle při implementaci v Javě (2004)
● Maximální kompatibilita s předchozími verzemi Javy ● C++ vyrábí pro každou hodnotu parametru novou třídu –
tudy se tvůrci Javy dát nemohli ● Jeden typ musí pracovat stejně dobře pro všechny hodnoty parametru
► Cíle při implementaci v .NET 2.0 (2005)
● Spojit maximum výhod C++ a Javy 6
Cíle generického programování ► Vyjádřit algoritmy s minimální závislostí na použitých datových strukturách ► Vyjádřit datové struktury s minimální závislostí na algoritmech ► Implementovat algoritmy co nejobecněji, aniž by při přechodu ke konkrétním typům došlo ke ztrátě efektivity ► Nevyhovuje-li obecná datová struktura pro speciální případy, poskytnout programátorovi možnost implementovat tyto speciální případy zvlášť ► Existuje-li k řešení určitého problému několik rovnocenných algoritmů, poskytnout programátorovi možnost implementovat je všechny a dát tak uživateli na vybranou podle dalších kritérií 7
Řešení v Javě ► Typové parametry generických typů slouží jako informace o typech objektů pouze pro překladač
● Rozhoduje o přípustnosti použitého typu ● V případě potřeby vloží potřebné přetypování
► Přeložený kód je od těchto informací „očištěn“ a žádné dodatečné typové informace již neobsahuje ► Programy je třeba psát tak, aby nebylo třeba za běhu rozhodovat podle hodnot typových parametrů ► Operace vyžadující znalost hodnot typových parametrů za běhu jsou zakázány – způsobí syntaktickou chybu ► Parametrizované typy si pamatují hodnoty typových parametrů svých rodičů
8
Řešení v .NET ► Řešení se liší podle specifiky datového typu
● „Hodnotové“ datové typy včetně primitivních
mají pro každý použitý typ definovaný speciální verzi (cesta C++) ● Lze to, protože jich je „konečně mnoho“
● Odkazové datové typy jsou řešeny podobně jako v Javě ► Výhody oproti Javě:
● Generické konstrukce lze používat i pro parametry primitivních typů
(Java se omezuje pouze na objektové typy) ● Instance si s sebou nesou informace o hodnotách typových parametrů
► Nevýhody oproti Javě
● Bylo třeba zcela přebudovat virtuální stroj i jazyk IL
(.NET byl ale v té době ještě „mládě“, tak to tak moc nevadilo) ● Nedají se použít žolíky ● U „hodnotových“ typů jsou přeložené třídy závislé na třídách, jež je používají, protože potřebují mít vytvořeny příslušné verze 9
Vaše jistota na trhu IT
Jednoduché použití
10
PT + TP – možná použití ► Deklarace atributu či lokální proměnné
● class Třída { T atribut; } ● Map mapa; ● List<String> seznam = new ArrayList<String>();
► Typ parametru či návratové hodnoty
● T max( Collection col ) { /*…*/ }
► Typ vyhozené výjimky
● public <E extends Exception> void metoda() throws E { /*…*/ }
► Potomek parametrizovaného předka
● class ChytrýSeznam extends ArrayList { /*…*/ }
11
Definice vlastního generického typu ► FrontaP = fronta s (typovým) parametrem
public class FrontaP<E> extends Object { List<E> prvky = new ArrayList<E>(); public FrontaP() {}
► Typ prvků ve frontě bude určen až při vytváření konkrétní instance fronty
public void zařaď( E prvek ) { prvky.add( prvek ); } public boolean isPrázdná() { return (prvky.size() == 0); }
► Různé instance mohu ukládat objekty různých typů
public E další() { E ret = prvky.get(0); prvky.remove(0); return ret; } public String toString() { return prvky.toString(); } } 12
Poznámky ► Seznam typových parametrů je součástí názvu třídy ► Parametr je možné použít všude, kde bychom použili příslušnou třídu ► Analogie s metodami: formální a skutečné parametry ► Hodnoty typových parametrů jsou známy pouze v době překladu => typové parametry nelze použít v situacích, kdy je třeba znát jejich hodnoty za běhu
13
Dědičnost parametrizovaných typů
► FrontaP = fronta s (typovým) parametrem
1/2
public class FrontaPI<E> extends FrontaP<E> implements Iterable<E> { public Iterator<E> iterator() { return new Iterator<E>() { int pořadí = 0; public boolean hasNext() { return (pořadí < prvky.size()); } public E next() { return prvky.get( pořadí++ ); }
► FrontaPI = FrontaP iterovatelná
public void remove() { prvky.remove( 0 ); } }; }
} 14
Dědičnost parametrizovaných typů
2/2
► Můžeme definovat potomka, který bude i nadále generický (příklad na minulé stránce), anebo parametrizovaného potomka (příklad na této stránce), tj. potomka s konkrétní hodnotou typového parametru ► V uvedené třídě není třeba definovat nic nového –hodnota typového parametru je pouze informaci pro překladač ► FrontaPIS = FrontaPI „stringů“ public class FrontaPIS extends FrontaPI<String> { //Nepotřebuje další upřesnění, vše říká TP (typový parametr) }
15
Metody s typovými parametry ► Jako generické můžeme definovat nejenom datové typy, ale i jednotlivé metody ► Typové parametry se uvádějí před typem návratové hodnoty ► Při volání takovéto metody si skutečnou hodnotu TP překladač často domyslí; pokud ne, musíme ji uvést ► Skutečnou hodnotu TP píšeme za kvalifikaci => potřebujeme-li uvést TP, musíme metodu kvalifikovat, i když by to jinak nebylo potřeba
16
Příklad generické metody public class Metody { public static List dvojice( T prvek ) { List ret = new ArrayList(); ret.add( prvek ); ret.add( prvek ); return ret; } public void volání() { Metody m = new Metody(); List<String> ls = dvojice( "Text" ); List