10. díl - Gettery a settery v Javě

Java Objektově orientované programování Gettery a settery v Javě

Unicorn College ONEbit hosting Tento obsah je dostupný zdarma v rámci projektu IT lidem. Vydávání, hosting a aktualizace umožňují jeho sponzoři.

V minulém dílu seriálu tutoriálů o Java jsem si vysvětlili statiku. Dnes se podíváme na tzv. gettery a settery.

Gettery a settery

Velmi často se nám stává, že chceme mít kontrolu nad změnami nějakého atributu objektu zvenčí. Budeme chtít atribut nastavit jako read-only nebo reagovat na jeho změny. Založme si nový projekt (název GetSet) a vytvořme následující třídu Student, která bude reprezentovat studenta v nějakém informačním systému.

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 = "jsem";
                if (!plnolety) {
                        jsemPlnolety = "nejsem";
                }
                String pohlavi = "muž";
                if (!muz) {
                        pohlavi = "žena";
                }
                return String.format("Jsem %s, %s. Je mi %d let a %s plnoletý.", jmeno, pohlavi, vek, jsemPlnolety);
        }

}

Třída je velmi jednoduchá, student se nějak jmenuje, je nějakého pohlaví a má určitý věk. Podle tohoto věku se nastavuje atribut plnolety pro pohodlnější vyhodnocování plnoletosti na různých místech systému. K uložení pohlaví používáme hodnotu boolean, zda je student muž. Konstruktor dle věku určí, zda je student plnoletý. Metoda toString() je navržena pro potřeby tutoriálu tak, aby nám vypsala všechny informace. V reálu by vrátila pravděpodobně jen jméno studenta. Pomocí konstruktoru si nějakého studenta vytvořme:

Student s = new Student("Pavel Hora", true, 20);
System.out.println(s);

Výstup:

Konzolová aplikace
Jsem Pavel Hora, muž. Je mi 20 let a jsem plnoletý.

Vše vypadá hezky, ale atributy jsou přístupné jak ke čtení, tak k zápisu. Objekt tedy můžeme rozbít například takto (hovoříme o nekonzistentním vnitřním stavu):

Student s = new Student("Pavel Hora", true, 20);
s.vek = 15;
s.muz = false;
System.out.println(s);

Výstup:

Konzolová aplikace
Jsem Pavel Hora, žena. Je mi 15 let a jsem plnoletý.

Určitě musíme ošetřit, aby se plnoletost obnovila při změně věku. Když se zamyslíme nad ostatními atributy, není nejmenší důvod, abychom taktéž umožňovali modifikovat pohlaví. Bylo by však zároveň vhodné je vystavit ke čtení, nemůžeme je tedy pouze nastavit jako private. V dřívějších dílech seriálu jsme k tomuto účelu používaly metody, které sloužily ke čtení privátních atributů. Jejich název jsme volili jako vratVek() a podobně. V Javě se všechny atributy, se kterými se má pracovat zvenčí, označují jako privátní a pro přístup k nim se definují právě podobné metody. K jejich pojmenování se ustálilo getNazevAtributu() pro čtení a setNazevAtributu() pro zápis. Pokud je atribut typu boolean, jmenuje se getter isNazevAtributu(). Třída by nově vypadala např. 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;
                // přehodnocení plnoletosti
                plnolety = true;
                if (vek < 18) {
                        plnolety = false;
                }
        }

        public boolean isPlnolety() {
                return plnolety;
        }

        @Override
        public String toString() {
                String jsemPlnolety = "jsem";
                if (!plnolety) {
                        jsemPlnolety = "nejsem";
                }
                String pohlavi = "muž";
                if (!muz) {
                        pohlavi = "žena";
                }
                return String.format("Jsem %s, %s. Je mi %d let a %s plnoletý.", jmeno, pohlavi, vek, jsemPlnolety);
        }

}

Metody, co hodnoty jen vracejí, jsou velmi jednoduché. Nastavení věku má již nějakou vnitřní logiku, při jeho změně musíme totiž přehodnotit atribut plnolety. Zajistili jsme, že se do proměnných nedá zapisovat jinak, než my chceme. Máme tedy pod kontrolou všechny změny atributů a dokážeme na ně reagovat. Nemůže se stát, že by nám někdo vnitřní stav nekontrolovaně měnil a rozbil. Všimněte si i změny v konstruktoru, kde se nastavuje věk metodou setVek().

Metodám k navrácení hodnoty se říká gettery a metodám pro zápis settery. Pro editaci ostatních atributů bychom udělali jednu metodu EditujStudenta, která by byla podobná konstruktoru.

Otázkou je, jaká je nyní výhoda toho, že je atribut jmeno privátní, když do něj jde zapisovat a lze z něj i číst. Vždyť máme v kódu zbytečně 2 metody, které tam zabírají místo a ještě je to pomalé?

Opravdu jsme to napsali správně, nebo alespoň tak, jak se to běžně dělá. Java před kompilací provádí četné optimalizace a pokud jsou metody tak jednoduché, že pouze vrací hodnotu nebo ji nastavují, metoda se zkompiluje jako přímý přístup do paměti. Jsou tedy stejně rychlé, jako kdybychom pracovali s veřejným atributem (za předpokladu, že setter nebo setter nemá nějakou další logiku).

IDE (NetBeans) dokáže gettery a settery automaticky generovat, nemusíme je tedy otrocky opisovat. Stačí na privátní proměnnou kliknout pravým tlačítkem a zvolit Refactor -> Encapsulate fields.

Automatické generování getterů a setterů v NetBeans IDE

V dalším dialogu si zaškrtneme, ke kterým atributům chceme vygenerovat gettery a ke kterým settery. My nebudeme chtít zpřístupnit pro zápis atribut plnolety a pohlavi. Atribut plnolety půjde změnit jen tak, že změníme věk studenta. Pohlaví nedává smysl měnit vůbec (pokud by to bylo opravdu někdy potřeba, byla by k tomu nějaká speciální metoda, aby se vyloučila změna chybou v kódu). Dialog potvrdíme.

Automatické generování getterů a setterů v NetBeans IDE

Optimalizace před kompilací odstranila rychlostní problém, který by jinak zbytečné volání metod způsobovalo. I zbytečné psaní metod jsme vykompenzovali jejich automatickým generováním. Otázkou zůstává, v čem je psaní metod oproti atributům výhodnější.

Hlavním důvodem je určitá standardizace. Nemusíme přemýšlet nad tím, jestli je daná vlastnost objektu řešena přes getter nebo atribut, na instanci jednoduše vždy voláme metodu začínající get (případně is) pokud chceme vlastnost instance číst, případně metodu začínající set, pokud ji chceme změnit.

Další výhodou je, že když se v budoucnu rozhodneme, že nějaký atribut chceme udělat read-only, jednoduše smažeme setter. Nemusíme vytvářet getter a měnit viditelnost atributu, což by změnilo rozhraní třídy a rozbilo existující kód, který by ji používal.

Gettery a settery tedy budeme odteď používat u všech atributů, které mají být zvenčí přístupné. V našich třídách se téměř nebudou vyskytovat atributy s viditelností public.

Zkusme si nyní ještě spustit kód, který předtím rozbil interní stav objektu:

Student s = new Student("Pavel Hora", true, 20);
s.setVek(15);
// s.setMuz(false); // Tento řádek musíme zakomentovat, jelikož se pohlaví již nedá zvenčí změnit
System.out.println(s);

Výstup je již v pořádku:

Konzolová aplikace
Jsem Pavel Hora, muž. Je mi 15 let a nejsem plnoletý.

Příště si představíme kolekci ArrayList, která je mnohem chytřejší než pole a ve které nejsme omezeni počtem prvků.


 

Stáhnout

Staženo 378x (17.45 kB)
Aplikace je včetně zdrojových kódů v jazyce java

 

  Aktivity (6)

Článek pro vás napsal David Čápka
Avatar
Autor pracuje jako softwarový architekt a pedagog na projektu ITnetwork.cz (a jeho zahraničních verzích). Velmi si váží svobody podnikání v naší zemi a věří, že když se člověk neštítí práce, tak dokáže úplně cokoli.
Unicorn College Autor se informační technologie naučil na Unicorn College - prestižní soukromé vysoké škole IT a ekonomie.

Jak se ti líbí článek?
Celkem (8 hlasů) :
55555


 


Miniatura
Předchozí článek
Cvičení k 9. lekci OOP v Javě
Miniatura
Následující článek
ArrayList v Javě

 

 

Komentáře
Zobrazit starší komentáře (51)

Avatar
mayo505
Redaktor
Avatar
mayo505:

chápeš ten operátor dobre, ale ten príklad čo napísal František a ja, sa uzátvorkuje inak ako si myslíš

pohlavi = muz ? "muž" : "žena"

sa uzátvorkuje ako

pohlavi = (muz ? "muž" : "žena")

Kde premenná muz je už tá podmienka, lebo je typu boolean. Dalo by sa to zapísať aj takto

pohlavi = (muz == true ? "muž" : "žena")
 
Odpovědět 17.4.2016 11:33
Avatar
hitzoR
Člen
Avatar
Odpovídá na mayo505
hitzoR:

Jasně, takhle se závorkama mi to smysl dává, ale ten původní příklad, který sem házel Kit mi přišel dost zvláštní.

 
Odpovědět 17.4.2016 12:18
Avatar
Debrax
Člen
Avatar
Debrax:

len drobný preklep - 2x setter
(za předpokladu, že setter nebo setter nemá nějakou další logiku)

 
Odpovědět 26.4.2016 10:49
Avatar
Nezmar Hydra
Člen
Avatar
Nezmar Hydra:

I když souhlasím s autorem, že nemá cenu pohlaví měnit, schválně jsem si tam setter přidal dodatečně. Pochopitelně se dopsal až na konec třídy a nepochopitelně to funguje. Jak to?
Když návrat řetězce je psán už před tím a getter taky. Vlastně všechny gettery se přidávají před settery. Přece nejdřív píšu do proměnných hodnotu a pak čtu proměnné .... .
Prosím pomožte staré struktuře pochopit nové objektové myšlení.

 
Odpovědět 10.6.2016 3:38
Avatar
Odpovídá na Nezmar Hydra
Robert Michalovič:

Špatně to chápeš.

  1. Jedná se o metody - pořadí jak jsou programovány či umístěny v kódu(v třídě) je naprosto irelevatní na běh programu (až na specifické případy např. statický cyklus, či jisté anotace v Java EE při běhu EE aplikací)
  2. Všechny metody jsou instační(objektové) takže nejdříve musíš vytvořit objekt což vytvoří konstruktor a ten provede vložení údajů. Teprve poté jsou ti dané metody k dispozici. Pokud by samozřejmě nedošlo k vytvoření údajů přes konstruktor, gettery vrátí null nebo hodí vyjímku. Schopností programátora je samozřejmě této situaci předcházet. Nemá umožnit získání údajů dříve než jsou vloženy.
  3. Pokud pořád tomu nerozumíš, CTRL+C a CTRL+V hod sem tvůj kód na kterém jsi to testoval a vysvětlíme si to řádek po řádku.
Editováno 10.6.2016 7:04
 
Odpovědět 10.6.2016 7:02
Avatar
Nezmar Hydra
Člen
Avatar
Nezmar Hydra:

Děkuji za pomoc. Ano špatně to chápu. Prostě si nedovedu představit jak ten program běží, když je irelevantí pořadí metod. Doteď jsem programoval jen strukurově ve VB a assembler PIC.
Představoal jsem si to tak, že když mám požadavek na objekt, stejně ho to sjede od zhora dolů a provede změny v proměnných.
Teď to vidím tak, že se provedou pouze ty změny s čím je proměnné svázána. Tedy jak jsem nastavil spojovací rouru, vstupní kohoutky ( settery) a výpustě (getery) Je to tak?
Pohraju si s programem v debugeru, nasázim si tam stopky a třeba to pochopim až řádek po řádku.

 
Odpovědět 10.6.2016 15:56
Avatar
Atrament
Člen
Avatar
Odpovídá na Nezmar Hydra
Atrament:

Ale tohle není strukturové programování, tohle je objektové programování, třída se neprovádí řádek po řádku od shora dolů, slouží k vytváření objektů, a na těch objektech se taky nevolají všechny metody jedna za druhou, ale voláš pouze ty, které potřebuješ volat. Settery se nevolají automaticky, jestli jsi to teda pochopil takto, ty voláš sám, když chceš nějaký atribut změnit.

 
Odpovědět 11.6.2016 0:34
Avatar
Odpovídá na Nezmar Hydra
Robert Michalovič:

No ten "požadavek na objekt "neexistuje"( tvz. metody se nemohou sami zavolat, až na specifické případy listenerů na objekt v GUI či JavaBeans). Ale tu myšlenku aplikuj na tu metodu. Objekt zavolá metodu a v té metodě to běží jak si představuješ. Tam už samozřejmě pořadí příkazů(instrukcí) hraje roli tvz. běží od vrchu dolů jak byli naprogramovány. Např. pokud bys měl metodu(vnitřní) v metodě(vnější). Tak samozřejmě běží nejdříve příkazy v metodě vnější než narazí na zavolání metody vnitřní kde se program vydá cestou příkazů té vnitřní. Samozřejmě se nejdříve dokončí ta vnitřní metoda až poté se dokončí ta vnější. ( toto opět nemusí platit pokud využíváš vlákna - to už je kapitola sama o sobě).

Na tyto věci nefunguje žádný rychlokurz, chce to svůj čas stovky, tisíce hodin programování než se ti to dostane pod kůži. Návody zde na webu nejsou vůbec špatné ale kniha pomůže taky dost.

 
Odpovědět 11.6.2016 7:52
Avatar
Nezmar Hydra
Člen
Avatar
Nezmar Hydra:

knihu už mám, ale stejně mi to pod kůži nějak neleze :o(

 
Odpovědět 19.6.2016 0:35
Avatar
Odpovědět 23. února 20:29
Vždy dostanu chuť zahrát si na foukací harmoniku theme song Rohanu, když mám dělat něco důležitého.
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 10 zpráv z 61. Zobrazit vše