7. diel - Dedičnosť a polymorfizmus
Dnes si opäť rozšírime znalosti o objektovo orientovanom programovaní. V
úvodnom tutoriále do OOP sme si hovorili, že OOP stojí na troch základných
pilieroch: zapuzdrenie, dedičnosti a
polymorfizmu. Zapúzdrenie a používanie modifikátora
private
nám je už dobre známe. Dnes sa pozrieme na zvyšné dva
piliere.
Dedičnosť
Dedičnosť je jedna zo základných vlastností OOP a slúži na tvorenie nových dátových štruktúr na základe starých. Vysvetlime si to na jednoduchom príklade.
Budeme programovať informačný systém. To je celkom reálny príklad, aby
sme si však učenie spríjemnili, bude to informačný systém pre správu
zvierat v ZOO:) Náš systém budú používať dva typy užívateľov:
používateľ a administrátor. Užívateľ je bežný ošetrovateľ zvierat,
ktorý bude môcť upravovať informácie o zvieratách, napr. ich váhu alebo
rozpätie krídiel. Administrátor bude môcť tiež upravovať údaje o
zvieratách a navyše zvieratá pridávať a mazať z databázy. Z atribútov
bude mať navyše telefónne číslo, aby ho bolo možné kontaktovať v
prípade výpadku systému. Bolo by určite zbytočné a neprehľadné, keby sme
si museli definovať obe triedy úplne celé, pretože mnoho vlastností týchto
2 objektov je spoločných. Užívateľ aj administrátor budú mať určite
meno, vek a budú sa môcť prihlásiť a odhlásiť. Nadefinujeme si teda iba
triedu Uzivatel
(nepôjde o funkčnú ukážku, dnes to bude len
teória, programovať budeme nabudúce):
Triedu som len naznačil, ale
iste si ju dokážeme dobre predstaviť. Bez znalosti dedičnosti by sme triedu
Administrator
definovali asi takto:
Vidíme, že máme v triede
množstvo redundantného (duplikovaného) kódu. Akékoľvek zmeny musíme teraz
vykonávať v oboch triedach, kód sa nám veľmi komplikuje. Teraz použijeme
dedičnosť, definujeme teda triedu Administrator
tak, aby z triedy
Uzivatel
dedila. Atribúty a metódy užívateľa teda už
nemusíme znovu definovať, Java nám ich do triedy sama dodá:
Vidíme, že na zdedenie sme
použili kľúčové slovo extends
. V anglickej literatúre
nájdete dedičnosť pod slovom inheritance.
V príklade vyššie nebudú v potomkovi prístupné privátne atribúty, ale
iba atribúty a metódy s modifikátorom public
. Atribúty
private
a metódy sú chápané ako špeciálna logika konkrétnej
triedy, ktorá je potomkovi utajená, aj keď ju vlastne používa, nemôže ju
meniť. Aby sme dosiahli požadovaný výsledok, použijeme nový
modifikátor prístupu protected
.
Modifikátor protected
sprístupní atribút alebo metódu buď
ľubovoľným potomkom v akomkoľvek balíčku alebo ľubovoľným triedam v tom
istom balíčku. Potomok sa teda už k atribútu dostane. Ako problém môže
byť, že atribút je zvonku viditeľný, preto sa v súbore .java
s triedou často používa iné meno balíčka, než v ktorom sú ostatné
časti programu. Tento nový balíček majú spoločný len súvisiace triedy
(napr. dedičnosťou), z balíčka v ktorom je hlavný program potom
protected
atribúty prístupné nebudú.
Pokiaľ by sme chceli atribúty alebo metódy sprístupniť iba triede samotnej a triedam v rovnakom balíčku, neuvedieme pred ne žiadny modifikátor prístupu (zvaný tiež ako package private).
Začiatok triedy Uzivatel
by teda vyzeral takto:
Keď si teraz vytvoríme
inštancie užívateľa a administrátora, obaja budú mať napr. atribút
jmeno
a metódu prihlasit()
. Java triedu
Uzivatel
zdedí a doplní nám automaticky všetky jej
atribúty.
Výhody dedenia sú jasné, nemusíme opisovať obom triedam tie isté atribúty, ale stačí dopísať len to, v čom sa líšia. Zvyšok sa zdedí. Prínos je obrovský, môžeme rozširovať existujúce komponenty o nové metódy a tým ich znova využívať. Nemusíme písať množstvo redundantného (duplikovaného) kódu. A hlavne – keď zmeníme jediný atribút v materskej triede, automaticky sa táto zmena všade zdedí. Nedôjde teda k tomu, že by sme to museli meniť ručne v 20 triedach a niekde na to zabudli a spôsobili chybu. Sme ľudia a chybovať budeme vždy, musíme teda používať také programátorské postupy, aby sme mali možnosťou chybovať čo najmenej.
O materskej triede sa niekedy hovorí ako o predkovi (tu
Uzivatel
) ao triede, ktorá z nej dedí, ako o potomkovi (tu
Administrator
). Potomok môže pridávať nové metódy alebo si
prispôsobovať metódy z materskej triedy (pozri ďalej). Môžete sa
stretnúť aj s pojmami nadtrieda a podtrieda.
Ďalšou možnosťou, ako objektový model navrhnúť, by
bolo zaviesť materskú triedu Uzivatel
, ktorá by slúžila iba na
dedenie. Z triedy Uzivatel
by potom dedili triedy
Osetrovatel
az neho Administrator
. To by sa však
vyplatilo pri väčšom počte typov užívateľov. V takom prípade hovoríme o
hierarchii tried, budeme sa tým zaoberať ku koncu tejto sekcie. Náš príklad
bol jednoduchý a preto nám stačili iba 2 triedy. Existujú tzv.
návrhové vzory, ktoré obsahujú osvedčené schémy
objektových štruktúr pre známe prípady použitia. Záujemcovia ich nájdu
popísané v sekcii Návrhové vzory, je to
však už pokročilejšia problematika a tiež veľmi zaujímavá. V objektovom
modelovaní sa dedičnosť znázorňuje graficky ako prázdna šípka smerujúca
k predkovi. V našom prípade by grafická notácia vyzerala takto:
Dátový typ pri dedičnosti
Obrovskou výhodou dedičnosti je, že keď si vytvoríme premennú s
dátovým typom materskej triedy, môžeme do nej bez problémov ukladať aj jej
potomkov. Je to dané tým, že potomok obsahuje všetko, čo obsahuje
materská trieda, spĺňa teda "požiadavky" (presnejšie obsahuje rozhranie)
dátového typu. A k tomu má oproti materskej triede niečo navyše. Môžeme
si teda urobiť pole typu Uzivatel
av ňom mať ako
používateľov, tak administrátorov. S premennou to teda funguje takto:
V Jave je mnoho konštrukcií, ako operovať s typmi inštancií pri dedičnosti. Podrobne sa na ne pozrieme počas seriálu, teraz si ukážme len to, ako môžeme overiť typ inštancie v premennej:
Pomocou operátora
instanceof
sa môžeme opýtať, či je objekt daného typu. Kód
vyššie otestuje, či je v premennej uzivatel
užívateľ alebo
jeho potomok administrátor.
Jazyky, ktoré dedičnosť podporujú, buď vedia dedičnosť jednoduchú, kde trieda dedí len z jednej triedy, alebo viacnásobnú, kde trieda dedí hneď z niekoľkých tried naraz. Viacnásobná dedičnosť sa v praxi príliš neosvedčila, časom si povieme prečo a ukážeme si aj ako ju obísť. Java podporuje iba jednoduchú dedičnosť, s viacnásobnou dedičnosťou sa môžete stretnúť napr. v C++.
Polymorfizmus
Nenechajte sa vystrašiť príšerným názvom tejto techniky, pretože je v
jadre veľmi jednoduchá. Polymorfizmus umožňuje používať jednotné
rozhranie na prácu s rôznymi typmi objektov. Majme napríklad mnoho objektov,
ktoré reprezentujú nejaké geometrické útvary (kruh, štvorec,
trojuholník). Bolo by určite prínosné a prehľadné, keby sme s nimi mohli
komunikovať jednotne, hoci sa líšia. Môžeme zaviesť triedu
GeometrickyUtvar
, ktorá by obsahovala atribút barva
a metódu vykresli
. Všetky geometrické tvary by potom dedili z
tejto triedy jej interface (rozhranie). Objekty kruh a štvorec sa ale
iste vykresľujú inak. Polymorfizmus nám umožňuje prepísať si
metódu vykresli()
u každej podtriedy tak, aby
robila, čo chceme. Rozhranie tak zostane zachované a my nebudeme
musieť premýšľať, ako sa to pri onom objekte volá.
Polymorfizmus býva často vysvetľovaný na obrázku so zvieratami, ktoré
majú všetky v rozhraní metódu speak()
, ale každé si ju
vykonáva po svojom:
Podstatou polymorfizmu je teda metóda alebo metódy, ktoré majú všetci potomkovia definované s rovnakou hlavičkou, ale iným telom.
Modifikátory prístupu
Pre prehľadnosť si ešte všetky modifikátory krátko popíšeme:
public
- Ľubovoľné triedy môžu pristupovať k tejto metóde alebo atribútu, teda aj z iných balíčkov.private
- Prístup má iba daná trieda, žiadna iná nemôže.protected
- Prístup má daná trieda a potomkovia triedy.- Žiadny modifikátor - Prístup majú všetky triedy v rovnakom balíčku
(napr.
package cz.itnetwork
). Teda podobné ako modifikátorpublic
, ale obmedzený na konkrétny balíček:)
Modifikátor | Daná trieda | Rovnaký balíček | Potomkovia triedy (odvodené triedy) | Svet (všetko ostatné) |
---|---|---|---|---|
public |
✔ | ✔ | ✔ | ✔ |
protected |
✔ | ✔ | ✔ | ❌ |
bez modifikátora | ✔ | ✔ | ❌ | ❌ |
private |
✔ | ❌ | ❌ | ❌ |
V nasledujúcom cvičení, Riešené úlohy k 5.-8. lekciu OOP v Jave, si precvičíme nadobudnuté skúsenosti z predchádzajúcich lekcií.