10. diel - Gettery a settery v Jave
V predchádzajúcom cvičení, Riešené úlohy k 9. lekcii OOP v Jave, sme si precvičili získané skúsenosti z predchádzajúcich lekcií.
Dnes sa v tutoriáli pozrieme na tzv. gettery a settery.
Gettery a settery
Veľmi často sa nám stáva, že chceme mať kontrolu nad zmenami nejakého
atribútu objektu zvonku. Budeme chcieť atribút nastaviť ako
read-only (len na čítanie) alebo reagovať na jeho zmeny. Založme si
nový projekt s názvom napr. GetSet
a vytvorme nasledujúcu triedu
Student
, ktorá bude reprezentovať študenta v nejakom
informačnom systéme:
class Student { public String jmeno; public boolean muz; public int vek; public boolean plnolety; public Student(String jmeno, boolean muz, int vek) { this.jmeno = jmeno; this.muz = muz; this.vek = vek; plnolety = true; if (vek < 18) { plnolety = false; } } @Override public String toString() { String jsemPlnolety = "som"; if (!plnolety) { jsemPlnolety = "nie som"; } String pohlavi = "muž"; if (!muz) { pohlavi = "žena"; } return String.format("Som %s, %s. Je mi %d rokov a %s plnoletý.", jmeno, pohlavi, vek, jsemPlnolety); } }
Trieda je veľmi jednoduchá, študent sa nejako volá, je nejakého pohlavia
a má určitý vek. Podľa tohto veku sa nastavuje atribút
plnolety
pre pohodlnejšie vyhodnocovanie plnoletosti na rôznych
miestach systému. Na uloženie pohlavia používame hodnotu typu
boolean
, či je študent muž. Konštruktor podľa veku určí, či
je študent plnoletý. Metóda toString()
je navrhnutá pre potreby
tutoriálu tak, aby nám vypísala všetky informácie. V reáli by vrátila
pravdepodobne len meno študenta. Pomocou konštruktora si nejakého študenta
vytvorme:
{JAVA_OOP} {JAVA_MAIN_BLOCK} Student student = new Student("Pavel Hora", true, 20); System.out.println(student); {/JAVA_MAIN_BLOCK} {/JAVA_OOP}
{JAVA_OOP} class Student { public String jmeno; public boolean muz; public int vek; public boolean plnolety; public Student(String jmeno, boolean muz, int vek) { this.jmeno = jmeno; this.muz = muz; this.vek = vek; plnolety = true; if (vek < 18) { plnolety = false; } } @Override public String toString() { String jsemPlnolety = "som"; if (!plnolety) { jsemPlnolety = "nie som"; } String pohlavi = "muž"; if (!muz) { pohlavi = "žena"; } return String.format("Som %s, %s. Je mi %d rokov a %s plnoletý.", jmeno, pohlavi, vek, jsemPlnolety); } } {/JAVA_OOP}
Výstup:
Konzolová aplikácia
Som Pavel Hora, muž. Je mi 20 rokov a som plnoletý.
Všetko vyzerá pekne, ale atribúty sú prístupné ako na čítanie, tak na zápis. Objekt teda môžeme rozbiť napríklad takto (hovoríme o nekonzistentnom vnútornom stave):
{JAVA_OOP} {JAVA_MAIN_BLOCK} Student student = new Student("Pavel Hora", true, 20); student.vek = 15; student.muz = true; System.out.println(student); {/JAVA_MAIN_BLOCK} {/JAVA_OOP}
{JAVA_OOP} class Student { public String jmeno; public boolean muz; public int vek; public boolean plnolety; public Student(String jmeno, boolean muz, int vek) { this.jmeno = jmeno; this.muz = muz; this.vek = vek; plnolety = true; if (vek < 18) { plnolety = false; } } @Override public String toString() { String jsemPlnolety = "som"; if (!plnolety) { jsemPlnolety = "nie som"; } String pohlavi = "muž"; if (!muz) { pohlavi = "žena"; } return String.format("Som %s, %s. Je mi %d rokov a %s plnoletý.", jmeno, pohlavi, vek, jsemPlnolety); } } {/JAVA_OOP}
Výstup:
Konzolová aplikácia
Som Pavel Hora, muž. Je mi 15 rokov a som plnoletý.
Určite musíme ošetriť, aby sa plnoletosť obnovila pri zmene veku. Keď
sa zamyslíme nad ostatnými atribútmi, nie je najmenší dôvod, aby sme
taktiež umožňovali modifikovať pohlavie. Bolo by však zároveň vhodné ich
vystaviť na čítanie, nemôžeme ich teda iba nastaviť ako
private
. V skorších dieloch seriálu sme na tento účel
používali metódy, ktoré slúžili na čítanie privátnych atribútov. Ich
názov sme volili ako vratVek()
a pre nastavenie atribútu
napríklad nastavVek()
. V Jave sa všetky atribúty, s ktorými sa
má pracovať zvonku, označujú ako privátne a pre prístup k nim sa definujú
práve podobné metódy. Na ich pomenovanie sa ustálilo
getNazevAtributu()
na čítanie a setNazevAtributu()
na zápis. Ak je atribút typu boolean
, volá sa getter
isNazevAtributu()
. Trieda by novo vyzerala napr. takto:
class Student { private String jmeno; private boolean muz; private int vek; private boolean plnolety; public Student(String jmeno, boolean pohlavi, int vek) { this.jmeno = jmeno; this.muz = pohlavi; setVek(vek); } public String getJmeno() { return jmeno; } public void setJmeno(String jmeno) { this.jmeno = jmeno; } public boolean isMuz() { return muz; } public int getVek() { return vek; } public void setVek(int vek) { this.vek = vek; // prehodnotenie plnoletosti plnolety = vek >= 18; } public boolean isPlnolety() { return plnolety; } @Override public String toString() { String jsemPlnolety = "som"; if (!plnolety) { jsemPlnolety = "nie som"; } String pohlavi = "muž"; if (!muz) { pohlavi = "žena"; } return String.format("Som %s, %s. Je mi %d rokov a %s plnoletý.", jmeno, pohlavi, vek, jsemPlnolety); } }
Metódy, čo hodnoty iba vracajú, sú veľmi jednoduché. Nastavenie veku
má už nejakú vnútornú logiku, pri jeho zmene musíme totiž prehodnotiť
atribút plnolety
. Zaistili sme, že sa do premenných nedá
zapisovať inak, než my chceme. Máme teda pod kontrolou všetky zmeny
atribútov a dokážeme na ne reagovať. Nemôže sa stať, že by nám niekto
vnútorný stav nekontrolovane menil a rozbil. Všimnite si aj zmeny v
konštruktore, kde sa nastavuje vek metódou setVek()
.
Metódam na vrátenie hodnoty sa hovorí gettery a metódam
pre zápis settery. Pre editáciu ostatných atribútov by sme
urobili jednu metódu editujStudenta()
, ktorá by bola podobná
konštruktoru.
Otázkou je, aká je teraz výhoda toho, že je atribút jmeno
privátne, keď do neho ide zapisovať a možno z neho aj čítať. Veď máme v
kóde zbytočne 2 metódy, ktoré tam zaberajú miesto a ešte je to
pomalé?
Naozaj sme to napísali správne, alebo aspoň tak, ako sa to bežne robí. Java pred kompiláciou vykonáva početné optimalizácie a pokiaľ sú metódy tak jednoduché, že iba vracajú hodnotu alebo ju nastavujú, metóda sa skompiluje ako priamy prístup do pamäte. Sú teda rovnako rýchle, ako keby sme pracovali s verejným atribútom (za predpokladu, že setter alebo getter nemá nejakú ďalšiu logiku).
IDE dokáže gettery a settery automaticky generovať, nemusíme ich teda otrocky opisovať. Stačí na privátnu premennú kliknúť pravým tlačidlom a zvoliť položku Refactor -> Encapsulate fields:
-
V ďalšom dialógu si zaškrtneme, ku ktorým atribútom chceme vygenerovať gettery a settery. My nebudeme chcieť sprístupniť pre zápis atribúty
plnolety
amuz
. Atribútplnolety
pôjde zmeniť iba zmenením veku študenta. Pohlavie nedáva zmysel meniť vôbec (pokiaľ by to bolo naozaj niekedy potrebné, bola by na to nejaká špeciálna metóda, aby sa vylúčila zmena chybou v kóde). Dialóg potvrdíme:Settery pre atribúty
plnolety
amuz
z triedy teda odstránime. -
V ďalšom dialógu si zaškrtneme, ku ktorým atribútom chceme vygenerovať gettery a ku ktorým settery. My nebudeme chcieť sprístupniť pre zápis atribúty
plnolety
amuz
. Atribútplnolety
pôjde zmeniť len tak, že zmeníme vek študenta. Pohlavie nedáva zmysel meniť vôbec (pokiaľ by to bolo naozaj niekedy potrebné, bola by na to nejaká špeciálna metóda, aby sa vylúčila zmena chybou v kóde). Dialóg potvrdíme:
Hlavným dôvodom je určitá štandardizácia. Nemusíme premýšľať nad
tým, či je daná vlastnosť objektu riešená cez getter alebo atribút, na
inštanciu jednoducho vždy voláme metódu začínajúcu slovom
get
(prípadne is
) pokiaľ chceme vlastnosť
inštancie čítať, prípadne metódu začínajúcu set
, ak ju
chceme zmeniť.
Ďalšou výhodou je, že keď sa v budúcnosti rozhodneme, že nejaký atribút chceme urobiť read-only (len na čítanie), jednoducho zmažeme setter. Nemusíme vytvárať getter a meniť viditeľnosť atribútu, čo by zmenilo rozhranie triedy a rozbilo existujúci kód, ktorý by ju používal.
Gettery a settery teda budeme odteraz používať pri všetkých
atribútoch, ktoré majú byť zvonku prístupné. V našich triedach
sa takmer nebudú vyskytovať atribúty s viditeľnosťou
public
.
Skúsme si teraz ešte spustiť kód, ktorý predtým rozbil interný stav objektu:
{JAVA_OOP} {JAVA_MAIN_BLOCK} Student student = new Student("Pavel Hora", true, 20); student.setVek(15); // student.setMuz(false); // Tento riadok musíme zakomentovať, pretože sa pohlavie už nedá zvonku zmeniť System.out.println(student); {/JAVA_MAIN_BLOCK} {/JAVA_OOP}
{JAVA_OOP} class Student { private String jmeno; private boolean muz; private int vek; private boolean plnolety; public Student(String jmeno, boolean pohlavi, int vek) { this.jmeno = jmeno; this.muz = pohlavi; setVek(vek); } public String getJmeno() { return jmeno; } public void setJmeno(String jmeno) { this.jmeno = jmeno; } public boolean isMuz() { return muz; } public int getVek() { return vek; } public void setVek(int vek) { this.vek = vek; // prehodnotenie plnoletosti plnolety = vek >= 18; } public boolean isPlnolety() { return plnolety; } @Override public String toString() { String jsemPlnolety = "som"; if (!plnolety) { jsemPlnolety = "nie som"; } String pohlavi = "muž"; if (!muz) { pohlavi = "žena"; } return String.format("Som %s, %s. Je mi %d rokov a %s plnoletý.", jmeno, pohlavi, vek, jsemPlnolety); } } {/JAVA_OOP}
Výstup je už v poriadku:
Konzolová aplikácia
Som Pavel Hora, muž. Je mi 15 rokov a nie som plnoletý.
V nasledujúcom kvíze, Kvíz - Dedičnosť, statika, gettery a settery v Jave OOP, si vyskúšame 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é 647x (9.02 kB)
Aplikácia je vrátane zdrojových kódov v jazyku Java