Polymorfizmus

<< Dedičnosť | Obsah | Abstraktné triedy >>

No hej, poviete si, ale ako my teraz budeme volať metódy dajUmiestnenie() ? Odpoveďou je polymorfizmus, alebo po slovensky viactvarovosť (slovenský ekvivalent nikto nepoužíva). Ide vlastne o dôsledok mechanizmu volania metód na objektoch. Keď cez niektorú premennú referencujúcu daný objekt zavoláme nejakú metódu, postupuje sa nasledovne:

  • Ak je v triede tohto objektu takáto metóda definovaná, zavolá sa
  • Ak nie, pozrieme sa do jej rodičovskej triedy. Ak sa tam táto metóda nachádza, vykoná sa, inak postupujeme v hierarchii vyššie, až kým na túto metódu nenarazíme a vykonáme ju.

Toto pravidlo platí bez ohľadu na to akého typu bola premenná referencujúca daný objekt. V predchádzajúcej kapitolke sme však povedali, že vieme volať iba metódy, ktoré nám umožní typ premennej. Dopíšeme si teda metódu dajUmiestnenie() aj do triedy Film. Reálne sa však táto metóda v našom programe nikdy nezavolá, keďže v poli filmy nebudeme mať objekty typu Film, ale iba objekty jej potomkov. Hovoríme tomu, že metódy v potomkoch prekrývajú túto metódu.

public class Film {
...
        //túto metódu potomkovia prekryjú
        public String dajUmiestnenie() {
                return "nemá umiestnenie";
        }
...
}

Situácia sa pridaním tejto metódy zmenila nasledovne:

Teraz si môžeme v triede ZoznamFilmov urobiť akýsi výpis filmov s ich umiestneniami. Každý objekt si zavolá metódu dajUmiestnenie() zo svojej triedy.

public class ZoznamFilmov {
        private Film[] filmy;
...            
        public void vypisFilmySUmiestnenim() {
                for (int i = 0; i < filmy.length; i++) {
                        System.out.println(filmy[i].getNazovFilmu());
                        System.out.println(filmy[i].dajUmiestnenie());
                }
        }
...
}

Malý bonus nakoniec: Ak by sme chceli aby aj metóda retazecPreVypis() vrátila okrem pôvodných informácií o filme aj informáciu o umiestnení, môžeme v triede Film modifikovať túto metódu tak, že v nej zavoláme metódu dajUmiestnenie() nasledovne:

public String retazecPreVypis() {
                String s = nazovFilmu + "\n";
                for (int i = 0 ; i < herci.length; i++) {
                        s = s + herci[i]+ " ";                 
                }
                s = s+ "\n";
                for (int i = 0 ; i < zanre.length; i++) {
                        s = s + zanre[i]+ " ";                 
                }
                s = s+ "\n";
                s = s+ "Hodnotenie: "+ hodnotenie + "\n";
                s = s+ "Dlzka filmu: "+ dlzkaFilmu + "\n";
                s = s+ dajUmiestnenie() + "\n"; // tu sa zavolá dajUmiestnenie() podľa typu objektu
                return s;
        }

Prečo je to tak? Lebo platí postup pri volaní metódy spomínaný za začiatku tejto kapitolky. Dá sa to predstaviť aj tak, že objekt si osvojí všetky metódy, ktoré dostal od svojej triedy, a z tried predkov si pozbiera postupne iba tie metódy, ktoré zatiaľ nemá. Teda ak sú nejaké metódy prekryté, použije tie, ktoré má v hierarchii dedenia k sebe bližšie.

V tomto prípade máme napríklad objekt triedy FilmNaDvd, tak tento objekt má metódu dajUmiestnenie() z triedy FilmNaDvd a metódu retazecPreVypis() z triedy Film lebo túto metódu potomkovia nemajú. Takže, keď táto osvojená metóda retazecPreVypis() zavolá dajUmiestnenie(), použije tú prekrytú metódu, ktorú si osvojil z triedy FilmNaDvd a nie metódu z triedy Film.

Finty pre komunikáciu s rodičovskou triedou

Niekedy sa stane, že z nejakého dôvodu chceme z oddedenej triedy (napr. FilmNaDvd) zavolať metódu z rodičovskej triedy (v tomto prípade Film) ktorú sme si v oddedenej triede prekryli. V našom príklade sme prekryli metódu dajUmiestnenie(). Ak chceme aj tak zavolať pôvodnú metódu použijeme slovíčko super, ktoré označuje rodičovskú triedu.

public class FilmNaDvd {
...
  public String dajPovodneUmiestnenie() {
     return "povodne:" + super.dajUmiestnenie();
  }
...
}