autor: Ivan Kupka

Verzia 1

home uvod K1 K2 K3 K4 K5 K6 K7 K8 K9 K10 K11 K12 K13 K14

Java 5

 

Dedičnosť

 

  Nadtriedy a podtriedy. Práca konštruktorov pri vytváraní prvkov podtried. Volanie členských premenných a metód nadtriedy. Kľúčové slovo “super”. Abstraktné triedy a metódy. Vstup z klávesnice.

 

 Dedičnosť je jeden zo spôsobov, ako možno použiť už raz vytvorený kód – A ako možno rozvinúť funkčnosť už raz vytvorených objektov rôznymi smermi, bez toho, že by tieto východiskové objekty museli byť zmenené. Navyše, niekedy je veľmi výhodné použiť znovu filozofiu východiskového objektu aj pri práci s jeho „potomkami“.  Triedy Chlieb, Mlieko, Kniha môžu byť napríklad potomkami triedy Tovar. Pri pokladni s prvkami týchto tried by mohla  pokladníčka narábať jednoducho ako s objektami typu Tovar – zistí si ich cenu a spracuje tento údaj. Inokedy nás zaujímajú tieto objekty aj pre ich špeciálnejšie vlastnosti. Buďme konkrétni:

 

 

Kódy P5.1 A-C

P5.1A:

public class Tovar {

         

          int cena;

         

          public Tovar(int kolkoStoji){

                    System.out.println("Hlasi sa konstruktor triedy Tovar.");

                    cena = kolkoStoji;

          }

         

         

          public static void main(String[] args){

         

            Tovar goods = new Tovar( 2);

                    System.out.println("Vytvoril tovar v cene " + goods.cena);

          }

}//EndOFClass

 

 

Výpis:

Hlasi sa konstruktor triedy Tovar.

Vytvoril tovar v cene 2

Process Exit...

P5.1B:

public class Kniha extends Tovar {

         

          String autor;

          String titul;

         

          public Kniha(String pisatel, String nazov, int cost){

                    // volanie konstruktora triedy Tovar:

                    super(cost);

                    System.out.println("Hlasi sa konstruktor triedy Kniha.");

              autor = pisatel;

            titul = nazov;      

          }

         

          public static void main(String[] args) {

         

           Kniha memoare1 = new Kniha("Dracula", "Moj vecny smad", 99);

           System.out.println("Vytvoril som knihu s titulom \"" +memoare1.titul +

                                              "\", cena je: " + memoare1.cena);

                                                 

          }//EOMain

}//EOClass

 

Výpis:

  Hlasi sa konstruktor triedy Tovar.

Hlasi sa konstruktor triedy Kniha.

Vytvoril som knihu s titulom "Moj vecny smad", cena je: 99

Process Exit...

 

 

 

P5.1C

 

 

public class Pokladnicka{

         

          public void povedzCenu(Tovar zbozie){

                    System.out.println("Tento tovar stoji " + zbozie.cena + " Sk");

          }

         

          public static void main(String[] args){

                    Tovar prvyTovar = new Tovar(48);

                    Kniha druhyTovar = new Kniha("Jack London","Martin Eden",198);

                   

                    Pokladnicka evicka = new Pokladnicka();

                    evicka.povedzCenu(prvyTovar);

                    evicka.povedzCenu(druhyTovar);

                   

                   

          }//EOMain

}//EOClass

 

 

Výstup:

Hlasi sa konstruktor triedy Tovar.

Hlasi sa konstruktor triedy Tovar.

Hlasi sa konstruktor triedy Kniha.

Tento tovar stoji 48 Sk

Tento tovar stoji 198 Sk

Process Exit...

 

Základná informácia o dedičnosti

  Dedičnosť umožňuje v Jave odvodzovať jednu triedu od druhej. Niekedy sa tento proces porovnáva aj s vkladaním: do nadtriedy (superclass) Tovar je vložená podtrieda (subclass) Kniha. Technicky sa toto odvodzovanie zapisuje slovom extends. Napr.: public class Kniha extends Tovar { ...

 T. j. podtrieda určítým spôsobom rozširuje svoju nadtriedu.

 Odvodené triedy majú prístup ku všetkým (nie súkromným) premenným a metódam nadtriedy. Môžu sa teda, okrem iného, správať ako prvok nadtriedy. Prvok triedy Kniha dedí všetky údaje a funkcionalitu zahrnutú v prvkoch triedy Tovar. Zároveň môže mať svoje nové, špecifické premenné a metódy. To, že trieda Tovar je nadtriedou inej triedy neznamená, že by nemohla byť bežne použitá. Viď kód v triede Pokladnicka.

 

Dedičnosť a konštruktory

  Ak chceme vytvoriť objekt podtriedy, v jeho konštruktore vždy najprv voláme (alebo Java volá za nás) konštruktor nadtriedy. Nami zvolený  konštruktor nadtriedy voláme pomocou slova super. Npríklad

ako v konštruktore triedy Kniha:

“super(cost)”;

Vo všeobecnosti   super( zoznamParametrov); je príkaz, volajúci konštruktor nadtriedy, ktorý sme jednoznačne určili práve udaním parametrov. Čo sa stane, ak žiaden konštruktor nadtriedy nezavoláme?

Ak zakomentujeme  v konštruktore triedy Kniha príkaz “super(cost)” a dame kód preložiť, prekladač vyhlási:

cannot resolve symbol

symbol  : constructor Tovar  ()

location: class Tovar

 

Prekladač sa teda správa, ako keby mal implicitne, pred všetkými príkazmi v konštruktore podtriedy Kniha vykonať najprv príkaz “super();”, ekvivalentný volaniu konštruktora Tovar() . Ten však v triede Tovar nie je definovaný. Akonáhle tam taký konštruktor pridáme:

public Tovar(){}

a Tovar.java a Kniha.java preložíme, znova všetko funguje. Metóda „Kniha.main()“ sa spustí bez problémov. Len jej výstup je asi iný, než sme čakali:

Hlasi sa konstruktor triedy Kniha.

Vytvoril som knihu s titulom "Moj vecny smad", cena je: 0

Process Exit...

 

Prirodzene, z formálneho hľadiska sa vytvoril aj podobjekt typu “Tovar” pre konštruovaný objekt „Kniha“, ale konštruktor Tovar()  nemá v sebe inštrukciu, aby nastavil cenu. Táto premenná preto ostáva na svojej defaultnej nulovej hodnote. Poučenie z celého procesu je jasné:

Za správne vytvorenie podobjektu a celého objektu, za použitie-volanie  vhodných konštruktorov  zodpovedá programátor.

 

 

Ďalší príklad. Ešte raz poradie inicializácie

 

 V triede Slovo sme vytvorili triedu, ktorá by mohla byť celkom prirodzene nadtriedou tried typu:

PodstatnéMeno, Sloveso, Predložka atď. Každý špecializovaný slovný typ má prirodzene iné vlastné „funkcie“ –  podstatné meno sa dá skloňovať, sloveso časovať atď. Začiatky takéhoto možného rozloženia triedy Slovo na podtriedy a jeho využitia vidíme na nasledujúcom príklade:

 

 

Kód P5.2

(Nadväzuje na Kód  P4.1 = Slovo)

public class PodstatneMeno extends Slovo{

          public String vzor = "neznamy";

         

          public PodstatneMeno(String text, String akyVzor){

           super(text);

           System.out.println("Konstruktor PodstatneMeno vidi vzor \" " + vzor+ "\"");

           this.vzor = akyVzor;      

          }

         

          public String toString(){

              return getText();

                    }

 

   public String piatyPadJednotnehoCisla(){

          if (vzor.equals("dub")){

            String sklonovane =getText() + "e";

            return        sklonovane;

          }

          if (vzor.equals("chlap")){

            String sklonovane =getText() + "ovi";

            return        sklonovane;

          }

    if (vzor.equals("zena")){

            String sklonovane =getText().substring(0,getText().length() -1) +"e";

            return        sklonovane;

          }

 

         

          return "Sorry, neviem vytvorit piaty pad, nepoznam taky vzor";

 

   }

         

public static void main(String[] args){

PodstatneMeno hrdina = new PodstatneMeno("zub","dub");

System.out.println("Podstatne meno je: " + hrdina);        

System.out.println("Jeho piaty pad je: " + hrdina.piatyPadJednotnehoCisla());

 

 hrdina = new PodstatneMeno("Tarzan","chlap");

System.out.println("Podstatne meno je: " + hrdina);        

System.out.println("Jeho piaty pad je: " + hrdina.piatyPadJednotnehoCisla());

 

PodstatneMeno hrdinka = new PodstatneMeno("Snehulienka","zena");

 

System.out.println("Teraz kratka rozpravka:");

System.out.println(hrdina + " snival o " + hrdinka.piatyPadJednotnehoCisla() + ".");

}       

         

}//EOClass

 

 

Všimnite si teraz výpis z programu. Usúďte z výpisu čo najviac o poradí, v akom sa konštruuje objekt triedy PodstatneMeno. Ako je to napríklad s tvorením jeho premennej „vzor“? Výpis z programu je:

 

Konstruktor Slovo vytvoril slovo s textom " zub" s dlzkou 3

Konstruktor PodstatneMeno vidi vzor " neznamy"

Podstatne meno je: zub

Jeho piaty pad je: zube

Konstruktor Slovo vytvoril slovo s textom " Tarzan" s dlzkou 6

Konstruktor PodstatneMeno vidi vzor " neznamy"

Podstatne meno je: Tarzan

Jeho piaty pad je: Tarzanovi

Konstruktor Slovo vytvoril slovo s textom " Snehulienka" s dlzkou 11

Konstruktor PodstatneMeno zapisal vzor " neznamy"

Teraz kratka rozpravka:

Tarzan snival o Snehulienke.

Process Exit...

 

 

Ako z výpisu vidno, pri tvorení objektu PodstatneMeno sa najprv vytvorí kompletný podobjekt typu Slovo. Potom prebehne inicializácia premenných inštancie. Premennej vzor je priradená hodnota „neznamy“. Konštruktor potom túto hodnotu upraví podľa zadaného parametra.

 

 

 

Len jedna nadtrieda

Každá nová podtrieda môže byť v Jave dedičom iba jednej nadtriedy.  Dedenie od viacerých tried paralelne, typu

public class Dieta extends Otec extends Matka {

teda neexistuje.

Každá podtrieda ale môže mať svojich vlastných potomkov. Vzniká tak viacstupňová hierarchia.

 

Kód P5.3

public class Detektivka extends Kniha {

         

          String menoDetektiva = "zatial sa nevie";

          String menoVraha     ="zatial sa nevie";

          int pocetMrtvol;

          boolean jeToKrvak;

         

          public Detektivka(String autor, String nazov, int cena, String detektiv, String vrah, int obete){

            super(autor,nazov,cena);        

            System.out.println("Tu konstruktor triedy Detektivka.");

            menoDetektiva = detektiv;

            menoVraha = vrah;

            pocetMrtvol = obete;

            if( obete > 10) jeToKrvak = true;

                   

          }

         

          public void dajInfo(){

                   

            System.out.println("\n Detektivka \"" + titul + "\", ktoru napisal renomovany autor " +

                                                 autor + "\n znova ukaze, ze detektiv " + menoDetektiva +

                                                  " odhali kazdeho vraha. \n Tentokrat rafinovaneho vraha " + pocetMrtvol + " obeti.");

        if(jeToKrvak){

          System.out.println("Osobam so slabsimi nervami odporucame citat v cakarni u lekara.");

        }                                                 

          }

                   

         

          public static void main(String[] args){

                    Detektivka haluze = new Detektivka("Markiz Fero", "Smrt v krovi", 432, "Oresiansky","Jozef Zahradnik", 13);

                    haluze.dajInfo();

                   

          }//EOMain

}//EOClass

Výstup :

Hlasi sa konstruktor triedy Kniha.

Tu konstruktor triedy Detektivka.

 

 Detektivka "Smrt v krovi", ktoru napisal renomovany autor Markiz Fero

 znova ukaze, ze detektiv Oresiansky odhali kazdeho vraha.

 Tentokrat rafinovaneho vraha 13 obeti.

Osobam so slabsimi nervami odporucame citat v cakarni u lekara.

Process Exit...

 

Skladanie, alebo dedičnosť?

 

 Ak chceme vytvoriť nové, bohatšie, funkčnejšie objekty, ktoré v sebe obsahujú funkcie iných, už existujúcich objektov, máme naporúdzi dve riešenia. Skladanie, alebo dedičnosť (poprípade postup príbuzný dedičnosti – použitie rozhrania).

   Pod skladaním sa rozumie využitie faktu, že objekt môže mať za členskú premennú ľubovoľný iný objekt.  Vezmime si ako príklad triedy String, Slovo a PodstatneMeno. Prečo sme napríklad PodstatneMeno definovali ako potomka triedy Slovo, ale Slovo sme nedefinovali ako potomka triedy String?  Odpoveď znie: preto, lebo každé slovo textovú reprezentáciu („má“ String), kdežto každé podstatné meno je slovo. Pri premýšľaní konkrétneho projektu a plánovaní tried, pri vyjasňovaní ich vzťahov  nám rozlišovanie pomocou kľúčových slov a je často pomôže.

 

Sprístupnenie premenných a metód nadtriedy pomocou kľúčového slova super

Niekedy meno premennej, alebo metódy v podtriede „prekryje“ rovnomennú premennú alebo metódu v nadtriede. Napríklad datumUkonceniaSkoly u vyštudovaného vysokoškoláka prekryje tú istú premennú, ktorú používal už  po maturite – vtedy ako vyštudovaný stredoškolák. Ku starej, prekrytej premennej, či metóde sa môžeme dopracovať pomocou kľúčového slova super. To ukazuje nasledujúci, trocha umelý, ale rukolapný príklad:

Kódy P5.4A-B

Kód P5.4A

public class Stredoskolak{

         

          int datumUkonceniaSkoly = 1973;

         

          String info = "Flakal sa ako sa dalo, ale nakoniec zmaturoval.";

         

         

          public void informuj(){

                   

          System.out.println("Student      ukoncil strednu skolu roku " +

                                                      datumUkonceniaSkoly + ". \n" + info);

          }

         

          public static void main(String[] args){

                    Stredoskolak Bill = new Stredoskolak();

               Bill.informuj();

          }//EOMain

}//EOClass

 

Výpis:

Student        ukoncil strednu skolu roku 1973.

Flakal sa ako sa dalo, ale nakoniec zmaturoval.

Process Exit...

Kód P5.4B

 

public class Vysokoskolak extends Stredoskolak{

           

          int datumUkonceniaSkoly = 1980;

         

          String info = "Trvalo mu to trocha dlhsie, ale vysledky mal uspokojive.";

         

         

          public void informuj(){

            System.out.println("Studium: " + super.datumUkonceniaSkoly +

                                         " - " + datumUkonceniaSkoly);

           super.informuj();

            System.out.println("Student ukoncil vysoku skolu roku " +

                                                      datumUkonceniaSkoly + ". \n" + info);

          }

          public static void main(String[] args){

                   

                    Vysokoskolak Bill = new Vysokoskolak();

                    Bill.informuj();

          // skuste potom toto:/Vysokoskolak Bill = new Stredoskolak();

// a toto: Stredoskolak Bill = new Vysokoskolak();

// a tiez: (Stredoskolak)Bill).informuj();

          }//EOMain

          }//EOClass

 

Výpis :

Studium: 1973 - 1980

Student        ukoncil strednu skolu roku 1973.

Flakal sa ako sa dalo, ale nakoniec zmaturoval.

Student ukoncil vysoku skolu roku 1980.

Trvalo mu to trocha dlhsie, ale vysledky mal uspokojive.

Process Exit...

 

Abstraktné metódy

 

   Predstavme si, že by sme potrebovali zostaviť program, ktorý by iba skloňoval podstatné mená. V takom prípade by stačilo vyjsť z materskej triedy podstatného mena a bolo by asi vhodné definovať-rozlišovať  jej potomkov podľa jediného kritéria.  Tým by bol vzor, podľa ktorého sa skloňujú.

Na druhej strane každé podstatné meno má prvý pád jednotného čísla, druhý pád množného čísla atď (vynechajme výnimky z pravidla). Tento fakt by sa mal už nejako objaviť pri navrhovaní materskej triedy.  Pri tomto navrhovaní ale ešte nemá zmysel implementovať napr. metódu siestyPadJednotnehoCisla(),  lebo ešte nevieme, ku akému vzoru bude potomok patriť. Ako programátori by sme však radi zabezpečili, aby každý z potomkov takúto funkciu k dispozícii mal. Riešením je použitie abstraktnej funkcie. Pozrime sa na to na príklade:

 

Kódy P5.5A-B

 

Kód5.5-A

 

  abstract class PodstMeno{

         

          private String text;

         

          PodstMeno(String textovaPodoba){

             text = textovaPodoba;                      

          }

         

          public String getText(){

             return text;         

          }

         

          abstract String prvyPadJednotnehoCisla();

          abstract String druhyPadJednotnehoCisla();

          // ......

          abstract String siestyPadMnoznehoCisla();

}//EOClass

 

 

 

Kód P5.5B

public class VzorDub extends PodstMeno{

         

          public VzorDub(String text){

            super(text);         

          }

         

    public String prvyPadJednotnehoCisla() {         

      return text;      

    }

   

    public String druhyPadJednotnehoCisla(){

      return text + "u";       

    }

          // ......

   public String siestyPadMnoznehoCisla(){

          return text + "mi";

  }

 

          public static void main (String[] args){

                   

            VzorDub slovo = new VzorDub("hrab");

            System.out.println("\n Sklonujem slovo " + slovo);

            System.out.println(slovo.prvyPadJednotnehoCisla());   

     System.out.println(slovo.druhyPadJednotnehoCisla());

     System.out.println("..............");    

     System.out.println("..............");    

   System.out.println(slovo.siestyPadMnoznehoCisla());  

 

  }//EOMain

}//EOClass

 

Výpis:

 

 Sklonujem slovo hrab

hrab

hrabu

..............

..............

hrabmi

Process Exit...

 

 

Všimnime si jednu vec:  Triedu PodstMeno sme nazvali „abstract class“. Každú triedu, ktorá obsahuje aspoň jednu abstraktnú metódu, treba takto označiť. Ak tak neurobíme, prekladač nás upozorní na chybu.

Abstraktné triedy slúžia len ako akási „prvotná forma“, určená na vytváranie konkrétnych potomkov. Preto nemôžeme vytvárať konkrétne objekty, ktoré by boli ich inštanciou. Riadok

PodstMeno instancia = new PodstMeno(“text“);

vyskytujúci sa v maine ľubovoľnej triedy prekladač okomentuje takto:

PodstMeno is abstract; cannot be instantiated

        PodstMeno instancia = new PodstMeno("text");

                              

Formátovaný Vstup

Aby sme mohli robiť zaujímavejšie programy, odbočme na chvíľu od problematiky dedičnosti. Vyrobme si metódu, ktorá nám umožní načítať údaje za behu programu: programu umožníme načítať riadkový reťazec dlý až niekoľko desiatok znakov. Všetky detaily spomenuté v triede Vstup si vysvetlíme až v nasledujúcich kapitolách. Teraz len  stručne opíšeme metódu „citaj()“, ktorú sa chystáme definovať.

Metóda vráti ako hodnotu textový reťazec typu String. Objekt „in“ je typu InputStream a je schopný načítať pole bajtov, ktoré odosielame, keď zadávame znaky z klávesnice. Každý znak sa načíta ako jeden bajt , vrátane znaku (znakov) ukončenia riadku. Pole bajtov premeníme na objekt typu String. Potom príkazom „trim()“  odrežeme ukončovacie a prázdne znaky. Podrobnejšie si prácu metódy „trim()“ pozrite v dokumentácii triedy String.

Aj v programoch iných tried z implicitného balíka budeme môcť teraz použiť príkaz  „Vstup.citaj()“, ktorý nám umožní načítavať údaje text za behu programu. Pozrime sa na nasledujúci kód:

 

Kód P5.6

 

import java.io.*;

 

  public class Vstup{

            public static String citaj(){

                    String nacitane = "zatialNenacitane";

                    try{

                              byte[] riadok = new byte[60];

                              System.in.read(riadok);

                              nacitane = new String(riadok).trim();

                              return nacitane;

                    }

                    catch(IOException e){

                              System.out.println("Chyba v nacitani!");

                              return nacitane;

                    }

                   

            }

         

         

          public static void main(String[] args){

                    System.out.println("Napis podstatne meno vzoru dub a daj enter:");

                    String podstMeno = Vstup.citaj();

                    System.out.println("Siesty pad slova " +

                                                      podstMeno + " je: "  + podstMeno + "e");

          }

         

}

 

Jeden z možných výpisov:

 

Napis podstatne meno vzoru dub a daj enter:

lob

Siesty pad slova lob je: lobe

Process Exit.

 

 

 

Cvičenia 5

 

Cv5.1 malý hlavolam

  Majú objekty podtriedy (odvodenej triedy, subclass) prístup k privátnym premenným a metódam svojej nadtriedy (materskej triedy, superclass)? V kóde P5.1A zmeňte premennú cena na súkromú (private). Zmažte príslušné preložené súbory Tovar.class, Kniha.class, Pokladnicka.class a dajte preložiť kódy P5.1A  - P5.1C  a sledujte, čo sa bude diať. Prípadné problémy odstráňte tak, aby výstup z programov bol rovnaký.

Riešenia

Cv.5.1

 Objekty podtriedy nemajú prístup k súkromným prvkom ani metódam svojej nadtriedy. V triede Tovar by premenná „cena“  mala byť rozhodne typu private. Ak sa už cena v konštruktore stanoví, zrejme by ju nemal len tak hocikto mať možnosť zmeniť. Problémy s prístupom sa dajú vyriešiť napríklad definovaním metódy public int  getCena() v triede Tovar a príslušným volaním tejto metódy v kódoch P5.1B-C.

Cv.5.2

V kóde P5.4B odkomentujte vždy jeden zo zakomentovaných riadkov a sledujte reakciu prekladača, poprípade výpis z programu.

 

Cv5.3

 Použite – rozšírte – kódy P5.5  a použite metódu Vstup.citaj() z kódu  P5.6

Na naprogramovanie triedy “Sklonuj”. Objekt tejto triedy bude schopný vypýtať si slovo, potom si vypýtať vzor. Ak vzor bude preňho známy (naprogramujte aspoň vzor dub), vyskloňuje podstatné meno podľa vzoru a vyskloňované tvary zadaného slova vytlačí.

 

Riešenia

Cv.5.1

 Objekty podtriedy nemajú prístup k súkromným prvkom ani metódam svojej nadtriedy. V triede Tovar by premenná „cena“  mala byť rozhodne typu private. Ak sa už cena v konštruktore stanoví, zrejme by ju nemal len tak hocikto mať možnosť zmeniť. Problémy s prístupom sa dajú vyriešiť napríklad definovaním metódy public int  getCena() v triede Tovar a príslušným volaním tejto metódy v kódoch P5.1B-C.

 

© Ivan Kupka

home uvod K1 K2 K3 K4 K5 K6 K7 K8 K9 K10 K11 K12 K13 K14

Vaše komentáre: kupka@fmph.uniba.sk