<< Textové súbory | Obsah | Vytvárame funkčné triedy >>
Predstavte si, že si chcete vytvoriť program na pohodlné vyhľadávanie vo vašej zbierke DVD filmov. O každom DVD viete:
Chceli by ste, aby váš program vedel:
Všimnite si dobre toto zadanie. Ako snáď pri všetkých programoch aj pri tomto si potrebujeme identifikovať dve základné množiny požiadaviek:
Tieto dve množiny požiadaviek sú úplne kľúčové. Pokiaľ si ich programátor neuvedomí, nemal by začať písať ani riadok kódu. Objektovo orientované programovanie zachováva tento princíp rozdelenia na dáta a funkcionalitu nie len pre celý program, ale aj pre jeho základné "súčiastky" - triedy a objekty.
V prvej polovici tejto prednášky sa budeme zaoberať prvou množinou požiadaviek - dátami
Keď máme ujasnenú prvú množinu požiadaviek, teda s akými dátami bude program pracovať, nasleduje prirodzene druhá otázka: "V akých štruktúrach to budem uchovávať?" Jednotlivé časti informácií by ste už asi vedeli uložiť vo vhodných premenných.
Dvdčkám by sme vedeli vymyslieť aj ďalšie vlastnosti ako tvar, farba, hmotnosť, cena a podobne. Tieto vlastnosti však nie je potrebné uvažovať, keďže s týmito informáciami podľa zadania zjavne nepotrebujeme pracovať. Zapamätajme si, že uchovávame iba užitočné vlastnosti, teda také, ktoré potrebujeme na zabezpečenie požadovanej funkcionality.
Keďže ideme pracovať s potenciálne veľkým počtom DVD nosičov, je vhodné, aby sme vedeli nejako povedať, že jedna sada týchto premenných patrí jednému DVD a iná sada inému DVD. Chceme teda nejako reprezentovať to, že jedno DVD si bude o sebe pamätať názov filmu, hercov, žánre, dĺžku filmu a hodnotenie.
S podobnou situáciou sme sa už stretli. Keď sme chceli aby si kresliaca plocha pamätala veľa korytnačiek, rozšírili sme triedu WinPane
a zadefinovali si v tejto novej triede inštančnú premennú referencujúcu pole korytnačiek. Keď sme chceli, aby si plocha pamätala dvojrozmerné pole pre piškôrky, opäť sme nejako rozšírili WinPane
.
V prípade DVD je neprirodzené rozširovať napríklad WinPane
na DVDPlochu alebo Turtle
na DVDKorytnačku, ktorá by predstavovala jedno DVD, lebo v prípade DVD nepotrebujeme pôvodné vlastnosti plochy či korytnačky.
V jave, keď chceme nejakú triedu vybudovať úplne od začiatku vytvárame rozšírenie triedy Object, ktorá je najoklieštenejšou triedou v jave. Napríklad aj pôvodná trieda Turtle
a WinPane
boli vytvorené rozšírením triedy Object, pretože všetky triedy sú vytvárané buď rozšírením triedy Object alebo rozšírením tried, ktorých pôvod má tiež korene v triede Object.
Vytvorme si triedu Dvd
, ktorá obaľuje všetky premenné, ktoré potrebujeme na uchovanie informácií o jednom DVD.
Ak vytvárame triedu ako rozšírenie triedy Object, nemusíme to expliticne písať. Nasledujúci zápis teda vyjadruje to isté ako predchádzajúci.
Už vieme že tieto premenné, ktoré sú definované v triede, ale nie v jej metódach, sa nazývajú inštančné premenné.
Keď už máme vytvorenú triedu Dvd
môžeme si vytvárať premenné typu Dvd
podobne, ako sme si vytvárali nové korytnačky:
Teraz by sme potrebovali o každom z týchto filmov uložiť informácie, ktoré si o filmoch chceme ukladať - teda názov filmu, hercov, žánre, dĺžku filmu a hodnotenie. Chceme teda naplniť inštančné premenné vytvorených objektov typu Dvd
.
Jeden zo spôsobov prístupu k inštančným premenným, a zároveň aj najmenej odporúčaný, je priamy prístup z vonku cez premennú objektu. Na to aby sme to umožnili, musíme im vymazať príznak private.
Budeme používať "bodkovú" notáciu. Podobne, ako keď voláme metódy na objektoch, môžeme za istých okolností (určite nesmú byť private) pristupovať cez bodku aj k inštančným premenným objektov.
Dĺžka filmu The Matrix je 136 minút
Predchádzajúci spôsob nie je odporúčaný, lebo porušuje princíp zapúzdrenosti, v ktorom sa objekty autonómne starajú o hodnoty svojich vlastných inštančných premenných. Pri priamom prístupe si objekty nevedia samy ochrániť hodnoty svojich inštančných premenných, čo môže ohroziť očakávanú funkcionalitu ďalších metód.
Povedzme, že naša trieda Dvd
, by mala metódu, ktorá pri výpise informácií o filme bude kresliť graficky ohodnotenie filmu napríklad počtom hviezdičiek. Keďže vieme, že hodnotenia sú v škále 0 až 10, prispôsobíme tomu veľkosť hviezdičiek a ich umiestnenie na ploche. A teraz si predstavte, že pri pridávaní nového filmu sa používateľ vášho programu preklepne a zadá namiesto desiatich sto hviezdičiek, alebo nejaký záporný počet.
Veľmi elegantne sa dá takéto prípady ošetriť tak, že hodnotu budeme nastavovať pomocou nastavovacej medódy - setter-a (z angličtiny setter = nastavovač, ale slovenský ekvivalent nikto nepoužíva). V tejto metóde si môžeme ošetriť správnosť nastavovanej hodnoty.
Potom keď napíšeme nasledujúci príkaz, hodnotenie
nám nedovolí zmeniť:
Hodnotenie 20.0 je mimo povolený rozsah [0,10]
Ochrana však v tomto prípade nie je dokonalá, lebo stále je možné meniť hodnotu priamo. Aby sme tomu zabránili, môžeme premennú hodnotenie
nastaviť ako súkromnú napísaním slovíčka private pred jej deklaráciu.
Teraz keby sme chceli zmeniť hodnotu z vonku priamo, nedovolilo by nám to. Eclipse nám nasledujúci riadok podčiarkne červenou čiarou s chybou "The field Dvd.hodnotenie is not visible"
Súkromné premenné môžu vidieť iba metódy v danej triede. Po nastavení hodnotenia, ako súkromnej premennej, nám teda nedovolí ani jej čítanie z vonku:
Na čítanie použijeme teda ďalšiu metódu vo vnútri triedy a to takzvaný getter (z angličtiny "getter" = odovzdávač, opäť slovenský ekvivalent nik nepoužíva).
Zavolanie tohto gettera je potom nasledovné:
Hodnotenie je 8.7
Systém getterov a setterov je veľmi často využívaný. Aby ste sa nebodaj neupísali k smrti, keď máte veľa inštančných premenných, Eclipse umožňuje gettery a settery vygenerovať cez menu Source -> Generate Getters and Setters, kde si môžete špecifikovať, pre ktoré inštančné premenné sa majú gettery a settery vygenerovať.
Našej triede Dvd
budeme odteraz chrániť jej inštančné premenné:
Naplnenie nového objektu typu Dvd
a jeho používanie može teraz prebiehať nasledovne:
Dĺžka filmu The Shawshank Redemption je 142
Napĺňanie inštančných premenných priamo pri vytváraní objektu nám umožňuje konštruktor. Konštruktor je špeciálny typ metódy, ktorá sa volá iba pri vytváraní (konštruovaní) nového objektu. Keď už objekt existuje, konštruktor preňho už nevoláme. Výsledkom zavolania konštruktora je nový objekt, ktorý môžeme typicky priradiť do nejakej premennej príslušného typu.
Volanie konštruktora sa nachádza za kľúčovým slovíčkom new. Konštruktor môže mať žiaden alebo viac parametrov.
Každá trieda má aspoň jeden konštruktor. Môže ich mať aj viac, ale musia sa líšiť počtom parametrov alebo typmi parametrov. Ak sa metódy alebo konštruktory volajú rovnako a líšia sa iba parametrami hovoríme o preťažovaní. V predchádzajúcom príklade sme volali až dva rôzne konštruktory triedy File, ktoré sa odlišovali počtom parametrov.
Ak v triede nie je napísaný žiaden koštruktor, použije sa skrytý, takzvaný implicitný konštruktor. Ide o koštruktor, ktorý nemá žiadne vstupné parametre, ani žiadne príkazy v tele. Prázdny konštruktor sa v prípade, že neexistujú iné konštruktory triedy, písať nemusí. POZOR! Ak existuje v triede nejaký konštruktor s parametrami, implicitný konštruktor sa automaticky nedoplní a ak ho chceme používať musíme si ho dopísať sami. Ak by sme chceli napísať prázdny konštruktor, vyzeral by takto:
Konštruktor je špeciálny typ metódy. Od normálnych metód sa líši nasledovne:
Dvd
Prvou úlohou každého, aj prázdneho, konštruktora je vytvoriť objekt, jeho inštančné premenné a naplniť inštančné premenné preddefinovanými hodnotami.
Preddefinovaná hodnota pre triedové premenné je null
. Premenné primitívnych typov null
obsahovať nemôžu, takže majú určené nasledovné hodnoty.
boolean
premenné majú preddefinovaný false
char
má preddefinovanú hodnotu '\u0000', ktorá je neviditeľným (NUL) znakom, ktorý sa v praxi nevyužíva.
V našom prípade, keď vytvoríme nový objekt triedy Dvd
cez prázdny konštruktor, máme v premenných nazovFilmu
, herci
a zanre
hodnotu null, v premennej dlzkaFilmu
hodnotu 0 a v premennej hodnotenie
hodnotu 0.0
Po vytvorení objektu môžeme konštruktor použiť na vhodnejšie nastavenie inštančných premenných. Využijeme na to niektorý konštruktor s parametrami.
Nový objekt triedy DVD potom vytvoríme volaním ľubovoľného (preťaženého) konštruktora.
Herci vo filme Fontána pre Zuzanu sú: Eva Vejmělková Jiří Bábek Robo Grigorov
Ak by ste potrebovali, tak konštruktor bez parametrov nemusí byť vždy prázdny. Nasledujúci konštruktor nastaví automaticky hodnotenie 5.0 pre každý objekt triedy Dvd
, ktorý bol vytvorený cez konštruktor bez parametrov.
POZOR! Do triedy môžeme napísať iba jeden konštruktor s rovnakým počtom, typmi a poradím typov parametrov. Takže nemôžete mať v triede aj prázdny konštruktor bez parametrov aj konštruktor bez parametrov, ktorý má v sebe nejaké príkazy.
Aj konštruktor sa bežne používa na ochranu hodnôt inštančných premenných. Prepíšeme si konštruktor na taký, ktorý robí rovnaké kontroly, ako sme písali v setteroch. V prípade, že vstupné hodnoty nebudú spĺňať požadované podmienky, nastavíme im nejaké preddefinované hodnoty.
Keďže aj konštruktor je riadny kus kódu, ktorý sa nám písať nechce, Eclipse prichádza s generátorom koštruktora. Koštruktor vygenerujete cez menu Source -> Generate Constructor using fields.
V kapitolke o koštruktoroch sme povedali, že prvou úlohou každého konštruktora je vytvoriť objekt, jeho inštančné premenné a naplniť inštančné premenné prednastavenými hodnotami. Vytváranie objektu reálne vykonáva konštruktor triedy Object a inštančné premenné sa doplňujú postupne volaním viacerých konštruktorov. Ako to vlastne funguje?
Musíme si uvedomiť, že tým, že naša trieda rozširuje nejakú inú triedu, tak okrem nových vecí musí vedieť aj všetko to, čo pôvodná trieda. Z toho dôvodu sa ako prvá vec pri vytváraní nového objektu vyrobí objekt, ktorý vie všetko to, čo by mu umožnila pôvodná trieda a potom naňho nabalíme schopnosti, ktoré má od našej triedy.
Keďže všetky triedy pôvodne pochádzajú z triedy Object, tak každý nový objekt najprv vznikne ako inštancia, ktorá má všetky schopnosti, ktoré mu poskytuje trieda Object a až potom sa na tieto schopnosti nabaľujú schopnosti (inštančné premenné a metódy), ktoré poskytujú rozširujúce triedy.
Prvá vec, čo urobí každý konštruktor je, že zavolá konštruktor rodičovskej triedy (t.j. z triedy z ktorej sa rozširuje). Tento rodičovský konštruktor zavolá konštruktor svojho rodiča a tak ďalej až kým sa nedostaneme k zavolaniu konštruktora triedy Object.
Ak sa na začiatku konštruktora neuvedie volanie rodičovského konštruktora, toto volanie sa uskutočňuje automaticky. Automatické volanie používa rodičovský konštruktor bez parametrov.
Pozrime sa na to, čo sa deje v kóde. Rodičovská trieda sa v jave označuje slovom super. Keď si vygenerujete konštruktor cez generátor konštruktora v Eclipse, ako prvý príkaz v konštruktore je super();
. Ak je tento príkaz uvedený, MUSÍ byť uvedený ako prvý príkaz v konštruktore. To je volanie rodičovského konštruktora - neznamená to, že rodičovská trieda sa volá super
.
Ak nechcete aby sa volal konštruktor rodičovskej triedy bez parametrov, môžete zavolať ľubovoľný jeden konštruktor s parametrami z rodičovskej triedy.
Je potrebné dodať, že niekedy sa konštruktor bez parametrov volať nedá a musíme volať rodičovský konštruktor s parametrami cez super(...). Je to vtedy, keď rodičovská trieda takýto konštruktor nemá a zároveň má nejaký konštruktor s parametrami t.j. implicitný konštruktor sa jej automaticky nevygeneroval.
Použitie si ukážeme na triede DvdNaPredaj
, ktorá rozširuje našu triedu Dvd
pridaním ceny.
Zosumarizujme, čo sa reálne deje pri volaní konštruktora: