Java - preťažovanie metód

Z Kiwiki
Skočit na navigaci Skočit na vyhledávání

Ako sme už povedali, v Jave dokážeme vytvárať hierarchiu tried. Teda hovoríme o nadradených a podradených triedach. Pojmom generická trieda sme označili triedu, ktorá definuje určité vlastnosti pre skupinu podriadených tried. Podtrieda dedí všetky verejné vlastnosti a metódy. Tu sa dostávame k jednej zaujímavosti: ak od nadradenej triedy zdedíme nejakú metódu, môžeme ju v triede potomka predefinovať? Odpoveď znie áno, a tento mechanizmus sa nazýva preťažovanie, resp. prekrývanie (overriding).

Definícia preťažovania

Princíp preťažovania metód je vo vytvorení novej metódy v podtiede, ktorá danú metódu zdedila od nadradenej triedy. V podtiede bude pôvodná metóda prekrytá (preťažená). Preťažená metóda musí mať rovnaký počet a typ argumentov ako pôvodná metóda. Poznámka: metódy označené ako final sa preťažovať nedajú.

Zoberme si príklad z predchádzajúcej časti Java - dedičnosť, kde sme definovali hierarchiu tried reprezentujúcu zvieracie druhy.

Do triedy Zviera doplníme metódu pohniSa, ktorá bude reprezentovať pohyb zvieraťa. Túto metódu zároveň definujme aj v triedach potomkov:

public class Zviera {
   // tu sa nachádza všetko z predchádzajúceho príkladu
   // doplníme nasledujúce:
   public void pohniSa(int k)
   {
     System.out.println("Presuvam sa o "+k+" metrov");
   }
}

public class Cicavec extends Zviera{

   public void pohniSa(int k)
   {
     System.out.println("Bezim "+k+" metrov");
   }
}

public class Plaz extends Zviera {

   public void pohniSa(int k)
   {
     System.out.println("Plazim sa"+k+" metrov");
   }
}

public class Vtak extends Zviera {

   public void pohniSa(int k)
   {
     System.out.println("Letím "+k+" metrov");
   }
}

public class Pes extends Cicavec {

   public void pohniSa(int k)
   {
     System.out.println("Skáčem ako pes "+k+" metrov");
   }
}

Pre otestovanie tejto konštrukcie môžeme použiť nasledujúci kód:

public class program {
    public static void main(String[] args) {
        Zviera a = new Zviera();
        Cicavec b = new Cicavec();
        Vtak c = new Vtak();
        Plaz d = new Plaz();
        Pes e = new Pes();
        //f je referencia na objekt Zviera, ale vytvorený bude objekt Pes 
        Zviera f = new Pes();

        System.out.println("Nazov: " + a.nazov);
        a.pohniSa(5);

        System.out.println("Nazov: " + b.nazov);
        b.pohniSa(5);

        System.out.println("Nazov: " + c.nazov);
        c.pohniSa(5);

        System.out.println("Nazov: " + d.nazov);
        d.pohniSa(5);

        System.out.println("Nazov: " + e.nazov);
        e.pohniSa(5);

        System.out.println("Nazov: " + e.nazov);
        f.pohniSa(4);
    }

Výpis programu je nasledujúci:

Nazov: Zviera
presúvam sa 5 metrov
Nazov: Cicavec
bežím 5 metrov
Nazov: Vtak
letím 5 metrov
Nazov: Plaz
plazím sa 5 metrov
Nazov: Pes
Skáčem ako pes 5 metrov
Nazov: Pes
Skáčem ako pes 4 metrov

Zaujímavosťou je tu vytvorenie objektu f. Objekt f je definovaný ako odkaz (referencia) na objekt Zviera, ale vytvorený je objekt typu Pes (new Pes). Preto pri zavolaní metódy pohniSa, sa zavolá metóda z triedy Pes a nie Zviera.

Rozhodovanie o tom, ktorá metóda sa v prípade obektu f vykoná, sa deje už pri kompilácii programu. Pri chybnom definovaní preťaženia nás teda informuje kompilátor. Uvažujme nasledujúci príklad:

class Zviera{
   public void pohniSa(int k)
   {
     System.out.println("Presuvam sa o "+k+" metrov");
   }
}

class Vtak extends Zviera{

   public void pohniSa(int k)
   {
     System.out.println("Letím "+k+" metrov");
   }

   public void spievaj(){
      System.out.println("Snažím sa spevať");
   }
}
//----program-pre-testovanie-funkcnosti-navrhnutych-tried
public class program {

    public static void main(String[] args) {
        Zviera a = new Zviera();      
        Zviera c = new Vtak();
      
        a.pohniSa(5);

        c.pohniSa(5);
        c.spievaj();      
    }
}

Kompilátor zahlási nasledujúcu chybu:

progam.java:30: cannot find symbol
symbol  : method spievaj()
location: class Zviera
                b.spievaj();
                 ^

Táto chyba je tu preto, pretože trieda Zviera nemá metódu spievaj. A objekt b je odkaz na triedu Zviera.

Pravidlá pre preťažovanie

  • Argumenty preťaženej metódy musia byť úplne rovnaké ako argumenty zdedenej metódy
  • Návratový typ preťažovanej metódy by mal byť rovnaký (alebo podtyp) ako návratový typ pôvodnej metódy v rodičovskej triede.
  • Úroveň prístupu nemôže byť reštriktívnejšia ako v pôvodnej triede. Napríklad: ak je v nadradenej triede metóda deklarovaná ako verejná (public), potom ako preťažené metóda nemôže byť private.
  • Metódy môže byť preťažené, len ak sú zdedené z nadtriedy.
  • Metóda deklarované ako final nemožno preťažovať.
  • Metóda deklarovaná ako statická (static) sa nedá preťažiť, ale dá sa redeklarovavať.
  • Ak sa metóda nedá zdediť, potom nemôže byť ani preťažené.
  • Konštruktory nemôžu byť preťažené.[1]

Použitie výrazu super

V predchádzajúcej časti (Java - dedičnosť) bolo povedané, že výraz super znamená nadradenú triedu. Výraz super() sme použili v konštruktore odvodenej triedy, aby sme zavolali konštruktor rodičovskej triedy. Výraz super však vo všeobecnosti znamená "nadradenú" triedu. Ukážeme to v nasledujúcom príklade:

class Zviera{
   public void pohniSa(int k)
   {
     System.out.println("Presuvam sa o "+k+" metrov");
   }
}

class Plaz extends Zviera{

   public void pohniSa(int k)
   {
     super.pohniSa(k);
     System.out.println("plazím sa "+k+" metrov");
   }
}
//-----------------------------------------------
public class program {

    public static void main(String[] args) {
        Zviera a = new Plaz();
        a.pohniSa(5);
    }
}

Výstup z programu bude nasledovný:

presúvam sa 5 metrov
plazím sa 5 metrov

Vysvetlenie: pri zavolaní metódy pohniSa sa začne vykonávať metóda pohniSa v triede Plaz. Ako prvé volanie je super.pohniSa(k);, čiže zavolá sa metóda pohniSa z nadradenej triedy.

Zdroje a odkazy

  1. tutorialspoint.com - Java Overriding http://www.tutorialspoint.com/java/java_overriding.htm