Lekce 4 - Hrací kostka v Javě - Zapouzdření a konstruktor
V předešlém cvičení, Řešené úlohy k 3. lekci OOP v Javě, jsme si procvičili nabyté zkušenosti z předchozích lekcí.
V dnešním tutoriálu začneme pracovat na slíbené aréně, ve které budou proti sobě bojovat dva bojovníci. Boj bude tahový (na přeskáčku) a bojovník vždy druhému ubere život na základě síly jeho útoku a obrany druhého bojovníka. Simulujeme v podstatě stolní hru, budeme tedy simulovat i hrací kostku, která dodá hře prvek náhodnosti. Začněme zvolna a vytvořme si dnes právě tuto hrací kostku. Zároveň se naučíme jak definovat vlastní konstruktor.
Základní pilíře OOP
OOP stojí na základních třech pilířích:
- Zapouzdření
- Dědičnost
- Polymorfismus
Dnes použijeme první z nich.
Vytvoření projektu
Vytvořme si nový projekt a pojmenujme ho TahovyBoj
. K projektu
si přidejme novou třídu s názvem Kostka
. Zamysleme se nad
atributy, které kostce dáme. Jistě by se hodilo, kdybychom si mohli zvolit
počet stěn kostky (klasicky 6 nebo 10 stěn, jak je zvykem u tohoto typu her).
Naše třída proto bude mít atribut pocetSten
. Třídu
Kostka
upravíme do následující podoby:
/** * Třída reprezentuje hrací kostku. */ public class Kostka { /** * Počet stěn kostky */ public int pocetSten; }
Konstruktory
Konstruktor je speciální metoda, která se sama zavolá ve chvíli
vytvoření instance objektu. Slouží k nastavení vnitřního stavu
objektu a k provedení případné inicializace. Přejdeme do souboru
TahovyBoj.java
, kde kostku vytvoříme tímto způsobem:
Kostka kostka = new Kostka();
Právě Kostka()
je konstruktor. Protože v naší třídě
žádný není, Java si sama vygeneruje prázdný konstruktor. My si však nyní
konstruktor do třídy přidáme. Deklaruje se jako metoda, ale nemá
návratový typ a musí mít stejný název jako je název
třídy (začíná tedy, na rozdíl od ostatních metod, velkým
písmenem), v našem případě tedy Kostka
. V konstruktoru
nastavíme počet stěn na pevnou hodnotu.
Přejdeme do souboru Kostka.java
a přidáme do třídy metodu
Kostka()
. V ní nastavíme hodnotu atributu
pocetSten
:
/** * Třída reprezentuje hrací kostku. */ public class Kostka { /** * Počet stěn kostky */ public int pocetSten; /** * Vytvoří novou instanci hrací kostky */ public Kostka() { pocetSten = 6; } }
Přesuňme se do souboru TahovyBoj.java
a vyzkoušejme si
vytvořit kostku a vypsat počet stěn:
{JAVA_OOP} {JAVA_MAIN_BLOCK} Kostka kostka = new Kostka(); // v tuto chvíli se zavolá konstruktor System.out.println(kostka.pocetSten); {/JAVA_MAIN_BLOCK} {/JAVA_OOP}
{JAVA_OOP} public class Kostka { public int pocetSten; public Kostka() { pocetSten = 6; } } {/JAVA_OOP}
V konzoli vidíme výstup:
Atribut pocetSten:
6
Vidíme, že se konstruktor opravdu zavolal.
Volitelný počet stěn
My bychom ale chtěli, abychom mohli u každé kostky při vytvoření
specifikovat, kolik stěn budeme potřebovat. Přejdeme do souboru
Kostka.java
a dáme tedy konstruktoru parametr:
public Kostka(int novyPocetSten) { pocetSten = novyPocetSten; }
Všimněte si, že jsme před název parametru metody přidali slovo
novy
, protože jinak by měl stejný název jako atribut a Javu by
to zmátlo. Vraťme se do souboru TahovyBoj.java
a zadejme tento
parametr do konstruktoru:
{JAVA_OOP} {JAVA_MAIN_BLOCK} Kostka kostka = new Kostka(10); // v tuto chvíli se zavolá konstruktor s par. 10 System.out.println(kostka.pocetSten); {/JAVA_MAIN_BLOCK} {/JAVA_OOP}
{JAVA_OOP} public class Kostka { public int pocetSten; public Kostka(int novyPocetSten) { pocetSten = novyPocetSten; } } {/JAVA_OOP}
V konzoli vidíme výstup:
Výstup s parametrem konstruktoru 10:
10
Vše funguje, jak jsme očekávali.
Výchozí hodnota kostky
Java nám již v tuto chvíli nevygeneruje prázdný (tzv.
bezparametrický) konstruktor, takže kostku bez parametru již
vytvořit nelze. My to však můžeme umožnit, vytvořme si další konstruktor
a tentokrát bez parametru. V něm nastavíme počet stěn na 6, protože
takovou hodnotu asi uživatel naší třídy u kostky očekává jako výchozí.
Přejdeme tedy zpátky do souboru Kostka.java
a vytvoříme
konstruktor bez parametru:
public Kostka() { pocetSten = 6; }
Třída Kostka
má tedy nyní dva konstruktory.
Zkusme si nyní vytvořit 2 instance kostky, každou jiným konstruktorem v
souboru TahovyBoj.java
:
{JAVA_OOP} {JAVA_MAIN_BLOCK} Kostka sestistenna = new Kostka(); Kostka desetistenna = new Kostka(10); System.out.println(sestistenna.pocetSten); System.out.println(desetistenna.pocetSten); {/JAVA_MAIN_BLOCK} {/JAVA_OOP}
{JAVA_OOP} public class Kostka { public int pocetSten; public Kostka() { pocetSten = 6; } public Kostka(int novyPocetSten) { pocetSten = novyPocetSten; } } {/JAVA_OOP}
Výstup:
Konzolová aplikace
6
10
Javě nevadí, že máme dvě metody se stejným názvem, protože jejich
parametry jsou různé. Hovoříme o tom, že metoda Kostka()
(tedy
zde konstruktor) má přetížení (overload). Toho můžeme
využívat i u všech dalších metod, nejen u konstruktorů. IDE nám
přehledně nabízí všechny přetížení metody ve chvíli, kdy zadáme její
název. V nabídce vidíme naše 2 konstruktory:

Mnoho metod v Javě má hned několik přetížení, zkuste se podívat
např. na metodu indexOf()
na třídě String
. Je
dobré si u metod projít jejich přetížení, abyste neprogramovali něco, co
již někdo udělal před vámi.
Ukážeme si ještě, jak jde obejít nepraktický název atributu u
parametrického konstruktoru (v našem případě novyPocetSten
) a
potom konstruktory opustíme. Problém je samozřejmě v tom, že když
napíšeme:
public Kostka(int pocetSten) { pocetSten = pocetSten; }
Java neví, kterou z proměnných myslíme, jestli parametr nebo atribut. V
tomto případě přiřazujeme do parametru znovu ten samý parametr. IDE nás
na tuto skutečnost dokonce upozorní. Uvnitř třídy se máme možnost
odkazovat na její instanci, je uložena v proměnné this
.
Využití si můžeme představit např. kdyby kostka měla metodu
dejHraci(Hrac hrac)
a tam by volala
hrac.seberKostku(this)
. Zde bychom hráči pomocí referenční
proměnné this předali sebe sama, tedy tu konkrétní kostku, se kterou
pracujeme. My se tím zde nebudeme zatěžovat, ale využijeme odkazu na
instanci při nastavování atributu:
public Kostka(int pocetSten) { this.pocetSten = pocetSten; }
Pomocí proměnné this
jsme specifikovali, že levá proměnná
pocetSten
náleží instanci, pravou Java chápe jako z parametru.
Máme tedy dva konstruktory, které nám umožňují tvořit různé hrací
kostky. Přejděme dál.
Zapouzdření
Zapouzdření umožňuje skrýt některé metody a atributy tak, aby zůstaly použitelné jen pro třídu zevnitř. Objekt si můžeme představit jako černou skřínku (anglicky blackbox), která má určité rozhraní (interface), přes které jí předáváme instrukce/data a ona je zpracovává.
Nevíme, jak to uvnitř funguje, ale víme, jak se navenek chová a používá. Nemůžeme tedy způsobit nějakou chybu, protože využíváme a vidíme jen to, co tvůrce třídy zpřístupnil.
Příkladem může být třída Clovek
, která bude mít atribut
datumNarozeni
a na jeho základě další atributy jako
plnolety
a vek
. Kdyby někdo objektu zvenčí změnil
datumNarozeni
, přestaly by platit proměnné plnolety
a vek
. Říkáme, že vnitřní stav objektu by byl
nekonzistentní. Toto se nám ve strukturovaném programování
může klidně stát. V OOP však objekt zapouzdříme. Atribut
datumNarozeni
označíme jako privátní a tím pádem bude jasné,
že nechceme, aby nám jej někdo jen tak měnil. Naopak ven vystavíme metodu
zmenDatumNarozeni()
, která dosadí nové datum narození do
proměnné datumNarozeni
a zároveň provede potřebný přepočet
věku a přehodnocení plnoletosti. Použití objektu je bezpečné a aplikace
stabilní.
Zapouzdření tedy tlačí programátory používat objekt jen tím
správným způsobem. Rozhraní (interface) třídy rozdělí na
veřejně přístupné (public
) a vnitřní strukturu
(private
).
Zapouzdření atributu
pocetSten
Minule jsme kvůli jednoduchosti nastavovali všechny atributy naší třídy
jako public
, tedy jako veřejně přístupné. Většinou se však
spíše nechce, aby se daly zvenčí modifikovat a používá se modifikátor
private
. Atribut je poté viditelný jen uvnitř třídy a zvenčí
se Java tváří, že vůbec neexistuje. Při návrhu třídy tedy nastavíme
vše na private
a v případě, že něco bude opravdu potřeba
vystavit, použijeme modifikátor public
. Naše třída nyní
vypadá takto:
/** * Vytvoří novou instanci hrací kostky */ public class Kostka { /** * Počet stěn kostky */ private int pocetSten;
Nyní nám nebude moci nikdo u již vytvořené kostky měnit počet stěn.
Umožníme však počet stěn zvenku přečíst. V souboru
Kostka.java
přidáme do třídy metodu
vratPocetSten()
, která nám vrátí hodnotu atributu
pocetSten
. Docílili jsme tím v podstatě toho, že je atribut
read-only (atribut není viditelný a lze ho pouze číst metodou, změnit ho
nelze). Nová metoda bude vypadat asi takto:
/** * Vrátí počet stěn hrací kostky * @return Počet stěn hrací kostky */ public int vratPocetSten() { return pocetSten; }
Přesuňme se do souboru TahovyBoj.java
a upravme výpis počtu
stěn na použití nové metody:
{JAVA_OOP} {JAVA_MAIN_BLOCK} Kostka sestistenna = new Kostka(); Kostka desetistenna = new Kostka(10); System.out.println(sestistenna.vratPocetSten()); System.out.println(desetistenna.vratPocetSten()); {/JAVA_MAIN_BLOCK} {/JAVA_OOP}
{JAVA_OOP} public class Kostka { private int pocetSten; public Kostka() { pocetSten = 6; } public Kostka(int pocetSten) { this.pocetSten = pocetSten; } public int vratPocetSten() { return pocetSten; } } {/JAVA_OOP}
Po spuštění programu v konzoli vidíme výstup:
Výstup metody vratPocetSten():
6
10
V příští lekci, Hrací kostka v Javě podruhé - Překrývání metod a random, se naučíme překrývat metody, pracovat s náhodnými čísly a dokončíme hrací kostku.
Měl jsi s čímkoli problém? Zdrojový kód vzorové aplikace je ke stažení každých pár lekcí. Zatím pokračuj dál, a pak si svou aplikaci porovnej se vzorem a snadno oprav.