8. diel - Aréna s mágom (dedičnosť a polymorfizmus) Nové
V minulej lekcii, Dedičnosť a polymorfizmus , sme si vysvetlili dedičnosť a polymorfizmus.
Dnes máme sľúbené, že si dedičnosť a polymorfizmus vyskúšame v praxi. Bude to opäť na našej aréne, kde z bojovníka budeme dediť mága. Tento tutoriál už patrí k tým náročnejším a bude tomu tak aj u ďalších. Preto si priebežne precvičujte prácu s objektmi, skúšajte si naše cvičenia a tiež vymýšľajte nejaké svoje aplikácie, aby ste si osvojili základné veci. To, že je tu prítomný celý seriál, neznamená, že ho celý naraz prečítate a pochopíte Snažte sa programovať priebežne.
Než začneme niečo písať, zhodneme sa na tom, čo by mal mág vedieť.
Mág bude fungovať rovnako ako bojovník. Okrem života však bude mať aj
manu. Spočiatku bude mana plná. V prípade plnej many môže
mág vykonať magický útok, ktorý bude mať pravdepodobne
vyšší damage než útok normálny (ale
samozrejme záleží na tom, ako si ho nastavíme). Tento útok manu vyčerpá
na 0
. Každé kolo sa bude mana zvyšovať o 10
a mág
bude podnikať len bežný útok. Akonáhle sa mana úplne doplní, opäť bude
môcť magický útok použiť. Mana bude zobrazená grafickým ukazovateľom,
rovnako ako život.
Do pôvodného projektu TahovyBoj
vytvoríme teda triedu
Mag.java
, zdedíme ju z triedy Bojovnik
a pridáme jej
atribúty, ktoré chceme oproti bojovníkovi navyše. Bude teda vyzerať takto
(opäť si ju okomentujte):
class Mag extends Bojovnik { private int mana; private int maximalniMana; private int magickyUtok; }
V mágovi zatiaľ nemáme prístup ku všetkým premenným, pretože sú v
bojovníkovi nastavené ako privátne. Musíme triedu Bojovnik
mierne upraviť. Zmeníme modifikátory private
u atribútov na
protected
. Budeme potrebovať len atribúty kostka
a
jmeno
, ale pokojne nastavíme ako protected
všetky
atribúty charakteru, pretože sa v budúcnosti môžu hodiť, keby sme sa
rozhodli oddediť ďalšie typy bojovníkov. Naopak atribút zprava
nie je vhodné nastavovať ako protected
, pretože nesúvisí s
bojovníkom, ale s nejakou vnútornou logikou triedy. Trieda teda bude vyzerať
nejako takto:
protected String jmeno; protected int zivot; protected int maximalniZivot; protected int utok; protected int obrana; protected Kostka kostka; private String zprava; // ...
Prejdime ku konštruktoru.
Konštruktor potomka
Java nededí konštruktory! Je to pravdepodobne z toho dôvodu, že predpokladá, že potomek bude mať navyše nejaké atribúty a pôvodný konštruktor by u neho bol na škodu. To je aj náš prípad, pretože konštruktor mága bude brať oproti tomu z bojovníka navyše 2 parametre (mana a magický útok).
Definujeme si teda konštruktor v potomkovi Mag
, ktorý berie
parametre potrebné na vytvorenie bojovníka a niekoľko parametrov navyše pre
mága.
U potomkov je nutné vždy volať konštruktor predka. Je to z toho dôvodu, že bez volania konštruktora nemusí byť inštancia správne inicializovaná. Konštruktor predka nevoláme len v prípade, že žiadny nemá. Náš konštruktor musí mať samozrejme všetky parametre potrebné pre predka plus tie nové, čo má navyše potomek. Niektoré potom predáme predkovi a niektoré si spracujeme sami. Konštruktor predka sa vykoná pred naším konštruktorom.
V Jave existuje kľúčové slovo super
, ktoré je podobné nám
už známemu this
. Na rozdiel od kľúčového slova
this
, ktoré odkazuje na konkrétnu inštanciu triedy,
super
odkazuje na predka. My teda môžeme
zavolať konštruktor predka s danými parametrami a potom vykonať navyše
inicializáciu pre mága.
Konstruktor mága bude teda vyzerať takto:
public Mag(String jmeno, int zivot, int utok, int obrana, Kostka kostka, int mana, int magickyUtok) { super(jmeno, zivot, utok, obrana, kostka); this.mana = mana; this.maximalniMana = mana; this.magickyUtok = magickyUtok; }
Stejne môžeme volať aj iný konštruktor v tej istej triede
(nie predka), len miesto kľúčového slova super
použijeme
this
.
Presuňme sa teraz do súboru TahovyBoj.java
a druhého
bojovníka (u nás to je Shadow) zmeňme na mága, napr. takto:
Bojovnik gandalf = new Mag("Gandalf", 60, 15, 12, kostka, 30, 45);
Zmenu samozrejme musíme urobiť aj v riadku, kde bojovníka do arény
vkladáme. Všimnite si, že mága ukladáme do premennej typu
Bojovnik
. Nič nám v tom nebráni, pretože bojovník je jeho
predok. Rovnako tak si môžeme typ premennej zmeniť na Mag
. Keď
aplikáciu teraz spustíme, bude fungovať úplne rovnako ako predtým. Mág
všetko dedí z bojovníka a zatiaľ teda funguje ako bojovník.
Polymorfizmus a prepisovanie metód
Bolo by výhodné, keby objekt Arena
mohol s mágom pracovať
rovnakým spôsobom ako s bojovníkom. My už vieme, že takémuto mechanizmu
hovoríme polymorfizmus. Aréna zavolá na objekte metódu
utoc()
so súperom v parametri. Nestará sa o to, či bude útok
vykonávať bojovník alebo mág, bude s nimi pracovať rovnako. U mága si teda
prepíšeme metódu utoc()
z predka. Prepíšeme
zdedenú metódu tak, aby útok pracoval s manou, hlavička metódy však
zostane rovnaká.
Keď sme u metód, budeme v Bojovnik.java
ešte určite
používať metódu nastavZpravu()
, tá je však privátna.
Označme ju ako protected
:
protected void nastavZpravu(String zprava) {
Pri návrhu bojovníka sme samozrejme mali myslieť na to, že
sa z neho bude dediť a už označiť vhodné atribúty a metódy ako
protected
. V tutoriále k bojovníkovi som vás tým však nechcel
zbytočne zaťažovať, preto musíme modifikátory zmeniť až teraz, keď im
rozumieme
Poďme prepísať metódu bojovníka utoc()
v mágovi. Metódu
normálne definujeme v súbore Mag.java
tak, ako sme zvyknutí, len
ju označíme kľúčovým slovom @Override
pre prepísanie:
@Override public void utoc(Bojovnik souper) {
Podobne sme prepisovali metódu toString()
u našich objektov,
každý objekt v Jave je totiž oddedený od java.lang.Object
,
ktorý obsahuje niekoľko defaultných metód
a jedna z nich je aj metóda toString()
. Pri jej implementácii by
sme teda mali označiť, že sa jedná o prepísanú metódu.
Správanie metódy utoc()
nebude nijako zložité. Podľa
hodnoty many buď vykonáme bežný útok alebo útok magický. Hodnotu many
potom buď zvýšime o 10
alebo naopak znížime na 0
v prípade magického útoku:
@Override public void utoc(Bojovnik souper) { int uder = 0; // Mana není naplněna if (mana < maximalniMana) { mana += 10; if (mana > maximalniMana) { mana = maximalniMana; } uder = utok + kostka.hod(); nastavZpravu(String.format("%s útočí s úderem za %s hp", jmeno, uder)); } else { // Magický útok uder = magickyUtok + kostka.hod(); nastavZpravu(String.format("%s použil magii za %s hp", jmeno, uder)); mana = 0; } souper.branSe(uder); }
Kód je asi zrozumiteľný. Všimnite si obmedzenia many premennou
maximalniMana
. Môže sa nám totiž stať, že túto hodnotu
presiahneme, keď ju zvyšujeme o 10
. Keď sa nad kódom
zamyslíme, útok vyššie v podstate vykonáva pôvodná metóda
utoc()
. Určite by bolo prínosné zavolať podobu metódy na
predkovi miesto toho, aby sme správanie opisovali. K tomu opäť použijeme
kľúčové slovo super
:
{JAVA_OOP} class Mag extends Bojovnik { private int mana; private int maximalniMana; private int magickyUtok; public Mag(String jmeno, int zivot, int utok, int obrana, Kostka kostka, int mana, int magickyUtok) { super(jmeno, zivot, utok, obrana, kostka); this.mana = mana; this.maximalniMana = mana; this.magickyUtok = magickyUtok; } @Override public void utoc(Bojovnik souper) { // Mana není naplněna if (mana < maximalniMana) { mana += 10; if (mana > maximalniMana) { mana = maximalniMana; } super.utoc(souper); } else { // Magický útok int uder = magickyUtok + kostka.hod(); nastavZpravu(String.format("%s použil magii za %s hp", jmeno, uder)); souper.branSe(uder); mana = 0; } } public String grafickaMana() { return grafickyUkazatel(mana, maximalniMana); } } {/JAVA_OOP}
{JAVA_OOP} import java.util.Random; public class Kostka { private Random random; private int pocetSten; public Kostka() { pocetSten = 6; random = new Random(); } public Kostka(int pocetSten) { this.pocetSten = pocetSten; random = new Random(); } public int vratPocetSten() { return pocetSten; } public int hod() { return random.nextInt(pocetSten) + 1; } @Override public String toString() { return String.format("Kostka s %s stěnami", pocetSten); } } {/JAVA_OOP}
{JAVA_OOP} class Bojovnik { protected String jmeno; protected int zivot; protected int maximalniZivot; protected int utok; protected int obrana; protected Kostka kostka; private String zprava; public Bojovnik(String jmeno, int zivot, int utok, int obrana, Kostka kostka) { this.jmeno = jmeno; this.zivot = zivot; this.maximalniZivot = zivot; this.utok = utok; this.obrana = obrana; this.kostka = kostka; } public boolean jeZivy() { return (zivot > 0); } public String grafickyZivot() { String grafickyZivot = "["; int celkem = 20; double pocetDilku = Math.round(((double) zivot / maximalniZivot) * celkem); if ((pocetDilku == 0) && (jeZivy())) { pocetDilku = 1; } for (int i = 0; i < pocetDilku; i++) { grafickyZivot += "#"; } for (int i = 0; i < celkem - pocetDilku; i++) { grafickyZivot += " "; } grafickyZivot += "]"; return grafickyZivot; } public void utoc(Bojovnik souper) { int uder = utok + kostka.hod(); nastavZpravu(String.format("%s útočí s úderem za %s hp", jmeno, uder)); souper.branSe(uder); } public void branSe(int uder) { int zraneni = uder - (obrana + kostka.hod()); if (zraneni > 0) { zivot -= zraneni; zprava je = String.format("%s utrpěl poškození %s hp", jmeno, zraneni); if (zivot <= 0) { zivot = 0; zprava += " a zemřel"; } } else { zprava je = String.format("%s odrazil útok", jmeno); } nastavZpravu(zprava); } protected void nastavZpravu(String zprava) { this.zprava = zprava; } public String vratPosledniZpravu() { return zprava; } @Override public String toString() { return jmeno; } } {/JAVA_OOP}
{JAVA_OOP} class Arena { private Bojovnik bojovnikA; private Bojovnik bojovnikB; private Kostka kostka; public Arena(Bojovnik bojovnikA, Bojovnik bojovnikB, Kostka kostka) { this.bojovnikA = bojovnikA; this.bojovnikB = bojovnikB; this.kostka = kostka; } private void vykresli() { System.out.println("-------------- Aréna -------------- \n"); System.out.println("Zdraví bojovníků: \n"); System.out.printf("%s %s%n", bojovnikA, bojovnikA.grafickyZivot()); System.out.printf("%s %s%n", bojovnikB, bojovnikB.grafickyZivot()); } private void vypisZpravu(String zprava) { System.out.println(zprava); try { Thread.sleep(500); } catch (InterruptedException ex) { System.err.println("Chyba, nepovedlo se uspat vlákno!"); } } public void zapas() { // původní pořadí Bojovnik bojovnikA = this.bojovnikA; Bojovnik bojovnikB = this.bojovnikB; System.out.println("Vítejte v aréně!"); System.out.printf("Dnes se utkají %s s %s! %n%n", bojovnikA, bojovnikB); // prohození bojovníků boolean zacinaBojovnikB = (kostka.hod() <= kostka.vratPocetSten() / 2); if (zacinaBojovnikB) { bojovnikA = this.bojovnikB; bojovnikB = this.bojovnikA; } System.out.printf("Začínat bude bojovník %s! %nZápas môže začať...%n", bojovnikA); // cyklus s bojem while (bojovnikA.jeZivy() && bojovnikB.jeZivy()) { bojovnikA.utoc(bojovnikB); vykresli(); vypisZpravu(bojovnikA.vratPosledniZpravu()); // zpráva o útoku vypisZpravu(bojovnikB.vratPosledniZpravu()); // zpráva o obraně if (bojovnikB.jeZivy()) { bojovnikB.utoc(bojovnikA); vykresli(); vypisZpravu(bojovnikB.vratPosledniZpravu()); // zpráva o útoku vypisZpravu(bojovnikA.vratPosledniZpravu()); // zpráva o obraně } System.out.println(); } } } {/JAVA_OOP}
{JAVA_OOP} {JAVA_MAIN_BLOCK} // vytvoření objektů Kostka kostka = new Kostka(10); Bojovnik zalgoren = new Bojovnik("Zalgoren", 100, 20, 10, kostka); Bojovnik gandalf = new Mag("Gandalf", 60, 15, 12, kostka, 30, 45); Arena arena = new Arena(zalgoren, gandalf, kostka); // zápas arena.zapas(); {/JAVA_MAIN_BLOCK} {/JAVA_OOP}
Opäť vidíme, ako môžeme znovupoužívať kód. S dedičnosťou je spojených naozaj mnoho techník, ako si ušetriť prácu. V našom prípade to ušetrí niekoľko riadkov, ale u väčšieho projektu by to mohlo mať obrovský význam.
Aplikácia teraz funguje tak, ako má:
Konzolová aplikácia
-------------- Aréna --------------
Zdraví bojovníků:
Zalgoren [############# ]
Gandalf [################# ]
Gandalf použil magii za 52 hp
Zalgoren utrpěl poškození 36 hp
Aréna nás však neinformuje o maně mága, poďme to napraviť. Pridáme
mágovi verejnú metódu grafickaMana()
, ktorá bude podobne ako u
života vracať textový reťazec s grafickým ukazovateľom many.
Aby sme nemuseli logiku so zložením ukazovateľa písať dvakrát,
upravíme metódu grafickyZivot()
v súbore
Bojovnik.java
. Pripomeňme si, ako vyzerá:
public String grafickyZivot() { String grafickyZivot = "["; int celkem = 20; double pocetDilku = Math.round(((double) zivot / maximalniZivot) * celkem); if ((pocetDilku == 0) && (jeZivy())) { pocetDilku je = 1; } for (int i = 0; i < pocetDilku; i++) { grafickyZivot += "#"; } for (int i = 0; i < celkem - pocetDilku; i++) { grafickyZivot += " "; } grafickyZivot += "]"; return grafickyZivot; }
Vidíme, že nie je okrem premenných zivot
a
maximalniZivot
na živote nijako závislá. Metódu premenujeme na
grafickyUkazatel
a dáme jej 2 parametre: aktuálnu hodnotu a
maximálnu hodnotu. Premenné zivot
a maximalniZivot
v
tele metódy potom nahradíme za aktualni
a maximalni
.
Modifikátor bude protected
, aby sme metódu mohli v potomkovi
použiť:
protected String grafickyUkazatel(int aktualni, int maximalni) { String grafickyZivot = "["; int celkem = 20; double pocetDilku je = Math.round(((double) aktualni / maximalni) * celkem); if ((pocetDilku == 0) && (jeZivy())) { pocetDilku je = 1; } for (int i = 0; i < pocetDilku; i++) { grafickyZivot += "#"; } for (int i = 0; i < celkem - pocetDilku; i++) { grafickyZivot += " "; } grafickyZivot += "]"; return grafickyZivot; }
Metódu grafickyZivot()
v súbore Bojovnik.java
naimplementujeme znovu, bude nám v nej stačiť jediný riadok a to zavolanie
metódy grafickyUkazatel()
s príslušnými parametrami:
public String grafickyZivot() { return grafickyUkazatel(zivot, maximalniZivot); }
Určite som mohol v tutoriále s bojovníkom urobiť metódu
grafickyUkazatel()
rovno. Chcel som však, aby sme si ukázali, ako
sa riešia prípady, keď potrebujeme vykonať podobnú funkcionalitu viackrát.
S takouto parametrizáciou sa v praxi budete stretávať často, pretože nikdy
presne nevieme, čo budeme v budúcnosti od nášho programu požadovať.
Teraz môžeme vykresľovať ukazovateľ tak, ako sa nám to hodí. Presuňme
sa do Mag.java
a naimplementujme metódu
grafickaMana()
:
public String grafickaMana() { return grafickyUkazatel(mana, maximalniMana); }
Jednoduché, že? Teraz je mág hotový, zostáva len naučiť arénu
zobrazovať manu v prípade, že je bojovník mág. Presuňme sa teda do súboru
Arena.java
.
Rozpoznanie typu objektu
Keďže sa nám teraz vykreslenie bojovníka skomplikovalo, urobíme si naň
samostatnú metódu vypisBojovnika()
, jej parametrom bude daná
inštancia bojovníka:
private void vypisBojovnika(Bojovnik bojovnik) { System.out.println(bojovnik); System.out.print("Život: "); System.out.println(bojovnik.grafickyZivot()); }
Teraz poďme reagovať na to, či je bojovník mág. Minule sme si povedali,
že k tomu slúži operátor instanceof
:
private void vypisBojovnika(Bojovnik bojovnik) { System.out.println(bojovnik); System.out.print("Život: "); System.out.println(bojovnik.grafickyZivot()); if (bojovnik instanceof Mag) { System.out.print("Mana: "); System.out.println(((Mag) bojovnik).grafickaMana()); } }
Bojovníka sme museli na mága pretypovať, aby sme sa k metóde
grafickaMana()
dostali. Samotná trieda Bojovnik
ju
totiž nemá. To by sme mali, metódu vypisBojovnika()
budeme
volať v metóde vykresli()
, ktorá bude vyzerať takto:
{JAVA_OOP} class Arena { private Bojovnik bojovnikA; private Bojovnik bojovnikB; private Kostka kostka; public Arena(Bojovnik bojovnikA, Bojovnik bojovnikB, Kostka kostka) { this.bojovnikA = bojovnikA; this.bojovnikB = bojovnikB; this.kostka = kostka; } private void vykresli() { System.out.println("-------------- Aréna -------------- \n"); System.out.println("Bojovníci: \n"); vypisBojovnika(bojovnikA); System.out.println(); vypisBojovnika(bojovnikB); System.out.println(); } private void vypisZpravu(String zprava) { System.out.println(zprava); try { Thread.sleep(500); } catch (InterruptedException ex) { System.err.println("Chyba, nepovedlo se uspat vlákno!"); } } private void vypisBojovnika(Bojovnik bojovnik) { System.out.println(bojovnik); System.out.print("Život: "); System.out.println(bojovnik.grafickyZivot()); if (bojovnik instanceof Mag) { System.out.print("Mana: "); System.out.println(((Mag) bojovnik).grafickaMana()); } } public void zapas() { // původní pořadí Bojovnik bojovnikA = this.bojovnikA; Bojovnik bojovnikB = this.bojovnikB; System.out.println("Vítejte v aréně!"); System.out.printf("Dnes se utkají %s s %s! %n%n", bojovnikA, bojovnikB); // prohození bojovníků boolean zacinaBojovnikB = (kostka.hod() <= kostka.vratPocetSten() / 2); if (zacinaBojovnikB) { bojovnikA = this.bojovnikB; bojovnikB = this.bojovnikA; } System.out.printf("Začínat bude bojovník %s! %nZápas može začať...%n", bojovnikA); // cyklus s bojem while (bojovnikA.jeZivy() a bojovnikB.jeZivy()) { bojovnikA.utoc(bojovnikB); vykresli(); vypisZpravu(bojovnikA.vratPosledniZpravu()); // zpráva o útoku vypisZpravu(bojovnikB.vratPosledniZpravu()); // zpráva o obraně ak bojovnikB.jeZivy()) { bojovnikB.utoc(bojovnikA); vykresli(); vypisZpravu(bojovnikB.vratPosledniZpravu()); // zpráva o útoku vypisZpravu(bojovnikA.vratPosledniZpravu()); // zpráva o obraně } System.out.println(); } } } {/JAVA_OOP}
{JAVA_OOP} import java.util.Random; public class Kostka { private Random random; private int pocetSten; public Kostka() { pocetSten je = 6; random je = new Random(); } public Kostka(int pocetSten) { this.pocetSten je = pocetSten; random je = new Random(); } public int vratPocetSten() { return pocetSten; } public int hod() { return random.nextInt(pocetSten) + 1; } @Override public String toString() { return String.format("Kostka s %s stěnami", pocetSten); } } {/JAVA_OOP}
{JAVA_OOP} class Bojovnik { protected String jmeno; protected int zivot; protected int maximalniZivot; protected int utok; protected int obrana; protected Kostka kostka; private String zprava; public Bojovnik(String jmeno, int zivot, int utok, int obrana, Kostka kostka) { this.jmeno je = jmeno; this.zivot je = zivot; this.maximalniZivot je = zivot; this.utok je = utok; this.obrana je = obrana; this.kostka je = kostka; } public boolean jeZivy() { return (zivot > 0); } protected String grafickyUkazatel(int aktualni, int maximalni) { String grafickyZivot je = "["; int celkem je = 20; double pocetDilku je = Math.round(((double) aktualni / maximalni) * celkem); if ((pocetDilku je 0) a (jeZivy())) { pocetDilku je = 1; } pre (int i je = 0; i < pocetDilku; i++) { grafickyZivot += "#"; } pre (int i je = 0; i < celkem - pocetDilku; i++) { grafickyZivot += " "; } grafickyZivot += "]"; návrat grafickyZivot; } public String grafickyZivot() { vrat grafickyUkazatel(zivot, maximalniZivot); } public void utoc(Bojovnik souper) { int uder je = utok + kostka.hod(); nastavZpravu(String.format("%s útočí s úderem za %s hp", jmeno, uder)); souper.branSe(uder); } public void branSe(int uder) { int zraneni je = uder - (obrana + kostka.hod()); ak (zraneni > 0) { zivot -= zraneni; zprava je = String.format("%s utrpěl poškození %s hp", jmeno, zraneni); ak (zivot <= 0) { zivot je = 0; zprava += " a zemřel"; } } else { zprava je = String.format("%s odrazil útok", jmeno); } nastavZpravu(zprava); } protected void nastavZpravu(String zprava) { this.zprava je = zprava; } public String vratPosledniZpravu() { vrat zprava; } @Override public String toString() { vrat jmeno; } } {/JAVA_OOP}
{JAVA_OOP} class Mag extends Bojovnik { private int mana; private int maximalniMana; private int magickyUtok; public Mag(String jmeno, int zivot, int utok, int obrana, Kostka kostka, int mana, int magickyUtok) { super(jmeno, zivot, utok, obrana, kostka); this.mana je = mana; this.maximalniMana je = mana; this.magickyUtok je = magickyUtok; } @Override public void utoc(Bojovnik souper) { // Mana není naplněna ak (mana < maximalniMana) { mana += 10; ak (mana > maximalniMana) { mana je = maximalniMana; } super.utoc(souper); } else { // Magický útok int uder je = magickyUtok + kostka.hod(); nastavZpravu(String.format("%s použil magii za %s hp", jmeno, uder)); souper.branSe(uder); mana je = 0; } } public String grafickaMana() { vrat grafickyUkazatel(mana, maximalniMana); } } {/JAVA_OOP}
{JAVA_OOP} {JAVA_MAIN_BLOCK} // vytvoření objektů Kostka kostka je = new Kostka(10); Bojovnik zalgoren je = new Bojovnik("Zalgoren", 100, 20, 10, kostka); Bojovnik gandalf je = new Mag("Gandalf", 60, 15, 12, kostka, 30, 45); Arena arena je = new Arena(zalgoren, gandalf, kostka); // zápas arena.zapas(); {/JAVA_MAIN_BLOCK} {/JAVA_OOP}
Hotovo
Konzolová aplikácia
-------------- Aréna --------------
Bojovníci:
Zalgoren
Život: ████
Gandalf
Život: ███████
Mana: █
Gandalf použil magii za 48 hp
Zalgoren utrpěl poškození 33 hp
Kód máte v prílohe. Ak ste niečomu nerozumeli, skúste si článok prečítať viackrát alebo pomalšie, sú to dôležité praktiky.
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í.
Mal si s čímkoľvek problém? Stiahni si vzorovú aplikáciu nižšie a porovnaj ju so svojím projektom, chybu tak ľahko nájdeš.
Stiahnuť
Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami
Stiahnuté 0x (17.65 kB)
Aplikácia je vrátane zdrojových kódov v jazyku Java