7. díl - Dědičnost a polymorfismus

Java Objektově orientované programování Dědičnost a polymorfismus

V minulém tutoriálu o Javě jsme dokončili naši arénu, simulující zápas dvou bojovníků. Dnes si opět rozšíříme znalosti o objektově orientovaném programování. V úvodním dílu do OOP jsme si říkali, že OOP stojí na třech základních pilířích: zapouzdření, dědičnosti a polymorfismu. Zapouzdření a používání modifikátoru private nám je již dobře známé. Dnes se podíváme na zbylé dva pilíře.

Dědičnost

Dědičnost je jedna ze základních vlastností OOP a slouží k tvoření nových datových struktur na základě starých. Vysvětleme si to na jednoduchém příkladu:

Budeme programovat informační systém. To je docela reálný příklad, abychom si však učení zpříjemnili, bude to informační systém pro správu zvířat v ZOO :) Náš systém budou používat dva typy uživatelů: uživatel a administrátor. Uživatel je běžný ošetřovatel zvířat, který bude moci upravovat informace o zvířatech, např. jejich váhu nebo rozpětí křídel. Administrátor bude moci také upravovat údaje o zvířatech a navíc zvířata přidávat a mazat z databáze. Z atributů bude mít navíc telefonní číslo, aby ho bylo možné kontaktovat v případě výpadku systému. Bylo by jistě zbytečné a nepřehledné, kdybychom si museli definovat obě třídy úplně celé, protože mnoho vlastností těchto 2 objektů je společných. Uživatel i administrátor budou mít jistě jméno, věk a budou se moci přihlásit a odhlásit. Nadefinujeme si tedy pouze třídu Uzivatel (nepůjde o funkční ukázku, dnes to bude jen teorie, programovat budeme příště):

class Uzivatel
{
        private String jmeno;
        private String heslo;
        private int vek;

        public boolean prihlasit(String heslo)
        {
                ...
        }

        public boolean odhlasit()
        {
                ...
        }

        public void nastavVahu(Zvire zvire)
        {
                ...
        }

        ...
}

Třídu jsem jen naznačil, ale jistě si ji dokážeme dobře představit. Bez znalosti dědičnosti bychom třídu Administrator definovali asi takto:

class Administrator
{
        private String jmeno;
        private String heslo;
        private int vek;
        private String telefonniCislo;

        public boolean prihlasit(String heslo)
        {
                ...
        }

        public boolean odhlasit()
        {
                ...
        }

        public void nastavVahu(Zvire zvire)
        {
                ...
        }

        public void pridejZvire(Zvire zvire)
        {

        }

        public void vymazZvire(Zvire zvire)
        {

        }

        ...
}

Vidíme, že máme ve třídě spoustu redundantního (duplikovaného) kódu. Jakékoli změny musíme nyní provádět v obou třídách, kód se nám velmi komplikuje. Nyní použijeme dědičnost, definujeme tedy třídu Administrator tak, aby z třídy Uzivatel dědila. Atributy a metody uživatele tedy již nemusíme znovu definovat, Java nám je do třídy sám dodá:

class Administrator extends Uzivatel
{
        private String telefonniCislo;

        public void pridejZvire(Zvire zvire)
        {

        }

        public void vymazZvire(Zvire zvire)
        {

        }

        ...
}

Vidíme, že ke zdědění jsme použili klíčové slovo "extends". V anglické literatuře najdete dědičnost pod slovem inheritance.

V příkladu výše nebudou v potomkovi přístupné privátní atributy, ale pouze atributy a metody s modifikátorem public. Private atributy a metody jsou chápány jako speciální logika konkrétní třídy, která je potomkovi utajena, i když ji vlastně používá, nemůže ji měnit. Abychom dosáhli požadovaného výsledku, použijeme nový modifikátor přístupu protected.

Protected zpřístupní atribut nebo metodu buď libovolným potomkům v jakémkoli balíčku nebo libovolným třídám v tom samém balíčku. Potomek se tedy již k atributu dostane. Jako problém může být, že atribut je zvenčí viditelný, proto se v souboru .java se třídou často používá jiné jméno balíčku, než ve kterém jsou ostatní části programu. Tento nový balíček mají společný jen související třídy (např. dědičností), z balíčku ve kterém je hlavní program potom protected atributy přístupné nebudou.

Začátek třídy Uzivatel by tedy vypadal takto:

class Uzivatel
{
        protected String jmeno;
        protected String heslo;
        protected int vek;

        ...

Když si nyní vytvoříme instance uživatele a administrátora, oba budou mít např. atribut jmeno a metodu prihlasit(). Java třídu Uzivatel zdědí a doplní nám automaticky všechny její atributy.

Výhody dědění jsou jasné, nemusíme opisovat oběma třídám ty samé atributy, ale stačí dopsat jen to, v čem se liší. Zbytek se podědí. Přínos je obrovský, můžeme rozšiřovat existující komponenty o nové metody a tím je znovu využívat. Nemusíme psát spousty redundantního (duplikovaného) kódu. A hlavně - když změníme jediný atribut v mateřské třídě, automaticky se tato změna všude podědí. Nedojde tedy k tomu, že bychom to museli měnit ručně u 20ti tříd a někde na to zapomněli a způsobili chybu. Jsme lidé a chybovat budeme vždy, musíme tedy používat takové programátorské postupy, abychom měli možností chybovat co nejméně.

O mateřské třídě se někdy hovoří jako o předkovi (zde Uzivatel) a o třídě, která z ní dědí, jako o potomkovi (zde Administrator). Potomek může přidávat nové metody nebo si uzpůsobovat metody z mateřské třídy (viz dále). Můžete se setkat i s pojmy nadtřída a podtřída.

Další možností, jak objektový model navrhnout, by bylo zavést mateřskou třídu Uzivatel, která by sloužila pouze k dědění. Z Uzivatel by potom dědili Osetrovatel a z něj Administrator. To by se však vyplatilo při větším počtu typů uživatelů. V takovém případě hovoříme o hierarchii tříd, budeme se tím zabývat ke konci této sekce. Náš příklad byl jednoduchý a proto nám stačily pouze 2 třídy. Existují tzv. návrhové vzory, které obsahují osvědčená schémata objektových struktur pro známé případy užití. Zájemci je naleznou popsané v sekci Návrhové vzory, je to však již pokročilejší problematika a také velmi zajímavá. V objektovém modelování se dědičnost znázorňuje graficky jako prázdná šipka směřující k předkovi. V našem případě by grafická notace vypadala takto:

Dědičnost objektů – grafická notace

Datový typ při dědičnosti

Obrovskou výhodou dědičnosti je, že když si vytvoříme proměnnou s datovým typem mateřské třídy, můžeme do ni bez problému ukládat i její potomky. Je to dané tím, že potomek obsahuje vše, co obsahuje mateřská třída, splňuje tedy "požadavky" (přesněji obsahuje rozhraní) datového typu. A k tomu má oproti mateřské třídě něco navíc. Můžeme si tedy udělat pole typu Uzivatel a v něm mít jak uživatele, tak administrátory. S proměnnou to tedy funguje takto:

Uzivatel u = new Uzivatel("Jan Novák", 33);
Administrator a = new Administrator("Josef Nový", 25);
// Nyní do uživatele uložíme administrátora:
u = a;
// Vše je v pořádku, protože uživatel je předek
// Zkusíme to opačně a dostaneme chybu:
a = u;

V Javě je mnoho konstrukcí, jak operovat s typy instancí při dědičnosti. Podrobně se na ně podíváme během seriálu, nyní si ukažme jen to, jak můžeme ověřit typ instance v proměnné:

Uzivatel u = new Administrator("Josef Nový", 25);
if (u instanceof Administrator)
        System.out.println("Je to administrátor");
else
        System.out.println("Je to uživatel");

Pomocí operátoru instanceof se můžeme zeptat, zda je objekt daného typu. Kód výše otestuje, zda je v proměnné u uživatel nebo jeho potomek administrátor.

Jazyky, které dědičnost podporují, buď umí dědičnost jednoduchou, kde třída dědí jen z jedné třídy, nebo vícenásobnou, kde třída dědí hned z několika tříd najednou. Vícenásobná dědičnost se v praxi příliš neosvědčila, časem si řekneme proč a ukážeme si i jak ji obejít. Java podporuje pouze jednoduchou dědičnost, s vícenásobnou dědičností se můžete setkat např. v C++.

Polymorfismus

Nenechte se vystrašit příšerným názvem této techniky, protože je v jádru velmi jednoduchá. Polymorfismus umožňuje používat jednotné rozhraní pro práci s různými typy objektů. Mějme například mnoho objektů, které reprezentují nějaké geometrické útvary (kruh, čtverec, trojúhelník). Bylo by jistě přínosné a přehledné, kdybychom s nimi mohli komunikovat jednotně, ačkoli se liší. Můžeme zavést třídu GeometrickyUtvar, která by obsahovala atribut barva a metodu vykresli. Všechny geometrické tvary by potom dědily z této třídy její interface (rozhraní). Objekty kruh a čtverec se ale jistě vykreslují jinak. Polymorfismus nám umožňuje přepsat si metodu vykresli u každé podtřídy tak, aby dělala, co chceme. Rozhraní tak zůstane zachováno a my nebudeme muset přemýšlet, jak se to u onoho objektu volá.

Polymorfismus bývá často vysvětlován na obrázku se zvířaty, která mají všechna v rozhraní metodu speak(), ale každé si ji vykonává po svém.

Polymorfismus

Podstatou polymorfismu je tedy metoda nebo metody, které mají všichni potomci definované se stejnou hlavičkou, ale jiným tělem. Polymorfismus si spolu s dědičností vyzkoušíme příště na bojovnících v naší aréně. Přidáme mága, který si bude metodu utoc() vykonávat po svém pomocí many, ale jinak zdědí chování a atributy bojovníka. Zvenčí tedy vůbec nepoznáme, že to není bojovník, protože bude mít stejné rozhraní. Bude to zábava :)


 

  Aktivity (2)

Článek pro vás napsal David Čápka
Avatar
Autor pracuje jako softwarový architekt a pedagog na projektu ITnetwork.cz (a jeho zahraničních verzích). Velmi si váží svobody podnikání v naší zemi a věří, že když se člověk neštítí práce, tak dokáže úplně cokoli.
Unicorn College Autor se informační technologie naučil na Unicorn College - prestižní soukromé vysoké škole IT a ekonomie.

Jak se ti líbí článek?
Celkem (13 hlasů) :
4.69234.69234.69234.69234.6923


 



 

 

Komentáře
Zobrazit starší komentáře (5)

Avatar
Kit
Redaktor
Avatar
Kit:

Můžeš modifikátor pouze rozšířit, tzn. z protected můžeš udělat public.

Odpovědět 1.7.2013 9:06
Vlastnosti objektů by neměly být veřejné. A to ani prostřednictvím getterů/setterů.
Avatar
Kit
Redaktor
Avatar
Kit:

Zkus psát programy tak, abys protected nepotřeboval. Všechny proměnné private nebo fiendly. Vnější metody public, ostatní private nebo friendly.

Odpovědět 1.7.2013 9:16
Vlastnosti objektů by neměly být veřejné. A to ani prostřednictvím getterů/setterů.
Avatar
Benjibs
Člen
Avatar
Odpovídá na Kit
Benjibs:

Takže jediným spôsobom, ako zmeniť modifikátor niečoho v triede potomka je tú konkrétnu vec reimplementovať? (ak ide o metódu)

U premenných, neexistuje niečo takéto? :
U rodiča:

protected String meno = "Jano";

U potomka:

public meno;

A potom by k premennej meno bol public prístupú.

Odpovědět 1.7.2013 9:17
1 + 1 = 2
Avatar
Kit
Redaktor
Avatar
Odpovídá na Benjibs
Kit:

Jenže ty nepotřebuješ public přístup k "meno". Neměl bys ho potřebovat.

Odpovědět 1.7.2013 9:21
Vlastnosti objektů by neměly být veřejné. A to ani prostřednictvím getterů/setterů.
Avatar
Jan Frindt
Člen
Avatar
Jan Frindt:

skvelý článok, skvelo vysvetlený

Odpovědět 9.3.2015 19:53
Aj keď je najškaredší, vždy miluj svoj kód! :D
Avatar
Marcel Smolik:

Dědičnost je spíše evoluce protože se program vyvíjí.

 
Odpovědět  -4 9.7.2015 17:11
Avatar
srdosm
Člen
Avatar
srdosm:

Mám k tomu takový dotaz. Mám třídu A, která dědí z třídy B.

Dále jinde v kódu pracuji s třídou A, mám vytvořený obj ( A a = new A(); ).
Mám metodu x(B b){...}, která jak je vidět bere jako argument třídu B. Proč, když tam dám třídu A si to neporadí (například píšu "x(new A())" )? A poradilo by si to obráceně, kdybych měl metodu x(A) a cpal jí x(b)?

 
Odpovědět 17.9.2015 19:18
Avatar
B42P6
Člen
Avatar
Odpovídá na srdosm
B42P6:

Neviem co ti nejde tak napisem nieco z dedicnosti. Predstav si triedu A a triedu B ktora dedi z A (B ma vsetko co A a este nieco naviac).Metoda berie ako parameter instanciu triedy A cize ak jej dame B ziska to co potrebuje=A, pretoze B obsahuje to co A a este nieco naviac.Predstav si druhu Metodu ktora berie ako parameter instancou triedy B , ak jej dame A nedame jej to co potrebuje=B (B obsahuje oproti A nieco naviac). Nie som na Pc tak sorry za diakritiku.

Odpovědět 17.9.2015 21:25
'long long long' is too long for GCC
Avatar
Vít Pelc
Člen
Avatar
Vít Pelc:

Studuji IT na technické škole, ale musím uznat, že až díky tohoto tutoriálu v Javě jsem pochopil programování (celkově). Moc dobře vysvětleno, díky autorovi za jeho čas a energii :)

 
Odpovědět 14. března 21:52
Avatar
Ondřej Burda:

Parádně sepsaný článek o dědičnosti a polymorfismu. David Čápka zachraňuje studenty i samouky :)

Odpovědět Včera 19:23
Escape the ordinary
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 10 zpráv z 15. Zobrazit vše