Ve čtvrté části seriálu o využití integrovaného vývojového prostředí Eclipse pro vývoj aplikací v programovacím jazyku Java si ukážeme, jaké další možnosti toto vývojové prostředí programátorům používajícím Javu nabízí. Nejprve se seznámíme s tím, jak lze automaticky (samozřejmě na explicitní žádost programátora) vytvořit takzvané settery i gettery pro vybrané atributy objektu či pro vybrané třídní atributy. Následně si ukážeme způsob automatické tvorby konstruktorů a taktéž tvorbu v mnoha případech velmi užitečné metody toString().
Problematika tvorby setterů a getterů v Javě
Většina programátorů vytvářejících aplikace v programovacím jazyku Java se již pravděpodobně setkala s následujícím problémem: potřebovali ve třídě obsahující poměrně velké množství atributů (či třídních atributů, tj. atributů s modifikátorem static) vytvořit pro každý atribut příslušný getter a popřípadě i setter. Připomeňme si, že se tímto názvem označují metody, jejichž názvy většinou začínají prefixem get/set, za nímž následuje název atributu zapsaný s velkým prvním písmenem (jméno samotného atributu přitom podle obecných zásad začíná písmenem malým). Pokud má například atribut objektu název name, bude jeho getter pojmenovaný getName() a setter bude mít jméno setName().
Příslušný getter přitom nemá žádné parametry a pouze vrací externí reprezentaci atributu (interní reprezentace může být samozřejmě odlišná, i když se většinou tělo getteru omezuje na jediný příkaz return). Podobně setter má jen jeden parametr, na jehož základě se mění interní reprezentace atributu, s případnými kontrolami předané hodnoty. Pro tvorbu tříd platí obecné doporučení, aby samotné atributy objektu nebyly vně své třídy viditelné, tj. aby například měly svou oblast viditelnosti nastavenou pomocí modifikátoru private, popř. alespoň protected nebo package protected. Čtení hodnoty takových atributů je zajišťováno příslušnými gettery a zápis (pokud je v daném kontextu vůbec vhodné měnit hodnotu atributu) je zajištěn příslušnými settery.
Toto řešení umožňuje změnu implementace objektu či vnitřní reprezentace atributů, aniž by se porušilo rozhraní deklarované ve třídě. Rozhraním třídy jsou zde myšleny atributy a jména metod, které jsou viditelné a dostupné zvenčí. Navíc je velmi jednoduché přidat do getterů i setterů například příkazy pro logování, doplnit programový kód pro kontrolu předávané hodnoty atd. Použití getterů a setterů má tedy (nikoli pouze v programovacím jazyku Java) svůj dobře odůvodněný význam, ovšem práce s nimi je v Javě poněkud problematická, protože tento programovací jazyk ve své současné verzi neobsahuje žádnou možnost, jak přímo u atributu zapsat (například s využitím nějaké anotace) příslušný setter a/nebo getter.
Navíc je ruční zápis getterů a setterů zdlouhavý a náchylný k chybám, zejména tehdy, pokud později dojde ke změně implementace třídy. Není tedy divu, že do integrovaného vývojového prostředí Eclipse byly doplněny funkce nabízející programátorům příkazy, které umožňují tuto prakticky nezbytnou, ale současně i jednotvárnou činnost vhodným způsobem provést automaticky. Jak lze settery a gettery na žádost programátora vytvořit, si ukážeme v navazujících kapitolách.
Příprava demonstračního projektu
Ještě předtím, než si vysvětlíme princip automatické tvorby setterů, getterů, konstruktorů, metody toString() atd., si musíme přímo ve vývojovém prostředí Eclipse připravit nový projekt nesoucí název Eclipse4, na němž se budou funkce IDE Eclipse pro automatickou tvorbu zdrojových textů testovat (jméno projektu bylo zvoleno podle pořadového čísla tohoto článku v rámci celého seriálu). Tento projekt bude obsahovat pouze dvě třídy.
První třída se bude jmenovat Person a necháme ji vytvořit prázdnou, tj. třída nebude zpočátku obsahovat žádné atributy ani metody. Druhá třída se pro jednoduchost bude jmenovat Main a při tvorbě kostry této třídy necháme integrované vývojové prostředí Eclipse automaticky vygenerovat i metodu main(), samozřejmě se správnými modifikátory (public static), argumenty (String args[]) i návratovou hodnotou (void). Postup vytvoření projektu Eclipse4 i obou tříd nazvaných Person a Main je naznačen na následujících pěti snímcích obrazovky.
Obrázek 1: Vytvoření nového projektu v integrovaném vývojovém prostředí Eclipse. Jedná se o běžný projekt psaný v Javě SE (Standard Edition), takže v příslušném „wizardu“ postačuje jen zadat jméno projektu a verzi Javy, pro kterou se má projekt přeložit.
Obrázek 2: Vytvoření nové třídy s názvem Person. Tato třída bude vytvořena v balíčku nazvaném org.tisnik.eclipse4, bude veřejně viditelná (public) a NEBUDE pro ni vytvořena metoda main() – viz též obsah spodních tří výběrových boxů v tomto formuláři.
Obrázek 3: Výsledek předchozích dvou kroků: V projektu Eclipse4 je vytvořen balíček org.tisnik.eclipse4, v němž je prozatím umístěna pouze jediná třída pojmenovaná Person, jejíž tělo je prázdné.
Obrázek 4: Vytvoření nové třídy s názvem Main. Tato třída taktéž bude ležet v balíčku nazvaném org.tisnik.eclipse4, bude veřejně viditelná a BUDE pro ni vytvořena metoda main().
Obrázek 5: Obě třídy vytvořené v rámci předchozích tří kroků zobrazené v samostatných editorech. Implicitně jsou sice všechny otevřené zdrojové texty sdruženy do jednoho podokna s taby („oušky“), ale přetažením tabu do pravé či naopak levé části hlavního okna Eclipse lze tab uvolnit a zobrazit vedle již existujícího a zobrazeného editoru. Tento způsob zobrazení budeme používat i na následujících snímcích obrazovky.
Automatické vytvoření setterů a getterů s využitím Eclipse
Na automatickém vytvoření setterů a getterů s využitím integrovaného vývojového prostředí Eclipse není v podstatě vůbec nic složitého. Programátor musí nejdříve zapsat deklarace všech potřebných atributů, přiřadit jim příslušnou oblast viditelnosti (například private), přesunout textový kurzor do deklarace třídy (to je velmi důležité a nesmí se na tuto „maličkost“ zapomenout) a posléze vybrat z hlavního menu Source příkaz Generate Getters and Setters.
Vývojové prostředí Eclipse následně zobrazí dialog, v jehož horní části se nachází strom obsahující jednotlivé atributy deklarované ve třídě. U každého atributu lze zvolit, zda se pro něj bude generovat getter a/nebo setter, a ve výběrovém seznamu pod stromem atributů je možné navíc určit, do kterého místa ve zdrojovém kódu se mají atributy zapsat (samozřejmě se vždy jedná o umístění v rámci zdrojového kódu jediné třídy). Celý postup automatického vygenerování atributů je zobrazen na následující sekvenci tří snímků obrazovky.
Obrázek 6: Nejprve je nutné ručně zapsat datové typy a jména všech atributů, pro něž se mají následně automaticky vytvořit gettery a settery. V našem demonstračním příkladu se jedná o atributy nazvané „name“, „surname“ a „age“, přičemž všechny atributy mají nastavenu oblast viditelnosti na „private“.
Obrázek 7: Druhým krokem je volba příkazu Source/Generate Getters and Setters z hlavního menu Eclipse (textový kurzor se přitom musí nacházet uvnitř těla třídy s atributy, jak jsme si již řekli). Po spuštění tohoto příkazu se zobrazí formulář, v němž je možné zvolit, pro které atributy se mají vytvořit gettery a/nebo settery. V seznamu „Insertion point“ lze zvolit, do které části zdrojového kódu se text s metodami getterů a setterů zapíše.
Obrázek 8: Výsledná podoba zdrojového kódu třídy Person po automatickém vytvoření všech vybraných getterů a setterů. Povšimněte si použití zápisu this.atribut u setterů. Tento zápis slouží pro odlišení jména parametru od jména atributu.
Vytvoření konstruktorů s inicializací atributů objektů
Další funkcí vývojového prostředí Eclipse, kterou lze využít mimo jiné i při práci s „hodnotovými“ objekty, je funkce sloužící pro automatické vytvoření konstruktorů, v jejichž těle se budou inicializovat vybrané atributy objektů na základě parametrů předaných konstruktoru. U poměrně velkého množství typů objektů je totiž výhodné provést inicializaci již při jejich vytváření, čímž se programátor může vyhnout nutnosti zdlouhavého nastavování atributů přes settery. Kromě kratšího zápisu mohou být konstruktory inicializující atributy použity pro vytvoření objektů předávaných jako parametr do jiné metody: nejakaMetoda(new MujObjekt(new File(PATH_TO_FILE))); atd.
Pojďme si nyní na sekvenci několika snímků obrazovky ukázat, jakým způsobem lze pro tvorbu těchto konstruktorů využít možností nabízených integrovaným vývojovým prostředím Eclipse. Opět využijeme třídu Person, v níž už byly v předchozím kroku vytvořeny settery a gettery:
Obrázek 9: Nejprve je nutné umístit textový kurzor kamkoli do definice třídy a následně z hlavního menu vybrat příkaz Source/Generate Constructor using Fields. Zobrazí se nový dialog, v němž je možné (podobně jako při tvorbě getterů a setterů) zvolit, které atributy mají být v konstruktoru inicializovány. Taktéž je možné vybrat, do které části zdrojového kódu se konstruktor vloží a jakou bude mít oblast viditelnosti (public, private...). V našem případě vybereme všechny tři atributy, ponecháme konstruktoru oblast viditelnosti public a nastavíme, že se nemá generovat zde přebytečné volání super().
Obrázek 10: Po stisku tlačítka „OK“ se konstruktor skutečně vytvoří a v jeho těle se provede přiřazení předaných parametrů k vybraným atributům. Aby bylo možné mít parametry pojmenované stejně jako atributy objektu, používá se pro přístup k atributům zápis this.jménoAtributu, podobně jako u setterů a getterů.
Obrázek 11: V rámci tréninku si můžeme úplně stejným postupem vytvořit další konstruktor, ovšem tentokrát se bude jednat o konstruktor bezparametrický. Postup vytvoření tohoto konstruktoru je stejný, pouze se v dialogu nevyberou žádné atributy objektu. Oblast viditelnosti konstruktoru zůstane nastavená na public.
Obrázek 12: Nový konstruktor bez parametrů je vytvořen a vložen do zdrojového kódu třídy Person.
Obrázek 13: Nyní je nutné provést ruční úpravy těla tohoto konstruktoru takovým způsobem, aby se volal druhý konstruktor vytvořený na obrázcích 9 a 10. Při volání druhého konstruktoru nelze použít jeho název (který je samozřejmě shodný s názvem třídy), ale namísto toho je nutné použít klíčové slovo „this“, které zde nahrazuje instanci vytvářené třídy. V editoru umístěném v pravém podokně je naznačeno, že oba vytvořené konstruktory lze skutečně použít.
Vygenerování metody toString()
Vývojové prostředí Eclipse nabízí vývojářům používajícím Javu ještě další funkce, které jim mohou ulehčit každodenní práci. Velmi často se programátoři například setkávají s požadavkem na přetížení metody toString() používané při převodu objektů na řetězec, například při „tisku“ objektů (resp. jejich stavu) pomocí příkazů System.out.print() a System.out.println(), při logování atd. Připomeňme si, že metoda toString() je deklarována již ve třídě nazvané Object, která v programovacím jazyku Java stojí na samém vrcholu hierarchie tříd (tato hierarchie tvoří stromovou strukturu, protože v Javě nelze použít vícenásobnou dědičnost).
V případě, že je metoda toString() deklarována v nějaké vývojářem vytvořené třídě, překryje se tím původní deklarace, která není ve velké části případů příliš užitečná, protože vytiskne pouze jméno třídy následované zavináčem a hexadecimálním vyjádřením vypočteného hešovacího kódu, který může, ale také vůbec nemusí souviset se stavem objektu.
Mnohem užitečnější je upravit metodu toString() takovým způsobem, aby se kromě jména třídy ve výsledném textu projevil i stav objektu, tj. hodnota jeho atributů. Samozřejmě je možné si tělo této metody napsat ručně (popř. řetězec skládat vně třídy s využitím getterů), ovšem s využitím funkcí nabízených Eclipse je to mnohem jednodušší a navíc bude tvar vytvářených řetězců dodržovat stále stejné schéma. Opět se podívejme na screenshoty, na nichž je celý postup ukázán:
Obrázek 14: Postup při tvorbě metody toString() je podobný postupu, který jsme si ukázali v předchozích kapitolách. Nejprve je nutné přemístit textový kurzor do deklarace třídy, pro niž se má metoda toString() vytvořit. Následně se v hlavním menu Eclipse vybere příkaz Source/Generate toString(), po jehož volbě se zobrazí dialog, který můžete vidět na tomto obrázku. Práce s tímto dialogem je velmi jednoduchá – postačuje vybrat ty atributy objektu, jejichž jména a hodnoty se mají objevit ve výsledném řetězci. Navíc lze také zvolit, aby se ve výsledném řetězci objevily hodnoty prvků uložených v atributu typu seznam či pole.
Obrázek 15: Po stisku tlačítka „OK“ se do zdrojového textu vloží deklarace metody toString(). Na tomto obrázku byla deklarace nepatrně upravena takovým způsobem, aby bylo celé tělo metody viditelné i v úzkém okně textového editoru.
Otestování vygenerovaných getterů, setterů, konstruktorů a metody toString()
Nyní je již námi vytvořená třída Person připravena pro jednoduché testy, protože pro každý svůj atribut obsahuje příslušný getter i setter, dále je v ní vytvořen konstruktor umožňující inicializovat všechny tři atributy objektu, bezparametrický konstruktor nastavující všechny atributy objektu na známou implicitní hodnotu a taktéž je možné kdykoli získat řetězec obsahující textovou reprezentaci stavu objektu, tj. řetězec, v němž jsou obsažena jména a hodnoty všech jeho atributů.
Obrázek 16: Ve třídě Main jsou vytvořeny dvě instance třídy Person. První instance (uložená do proměnné p1) používá pro inicializaci všech svých atributů přímo konstruktor, zatímco v případě druhé instance jsou nejdříve v bezparametrickém konstruktoru nastaveny všechny atributy na implicitní hodnotu a posléze jsou přenastaveny s využitím příslušných setterů. Následně jsou oba objekty vytištěny pomocí System.out.println(). V tomto případě je nejprve pro každou instanci třídy Person zavolána metoda toString() a teprve její výsledek – řetězec – je skutečně vytištěn. To znamená, že metodu toString() není v tomto případě nutné volat explicitně.
Obrázek 17: Ve spodní části okna Eclipse je zobrazen výsledek běhu našeho demonstračního příkladu. Stav každé instance třídy Person je vytištěn takovým způsobem, že je nejdříve zobrazeno jméno třídy a za ním se v hranatých závorkách nachází jména a hodnoty jednotlivých atributů.
Obsah následující části seriálu
V následující části seriálu o použití integrovaného vývojového prostředí Eclipse pro tvorbu programů v Javě si ukážeme automatickou tvorbu metod hashCode() a equals(), které se velmi často musí deklarovat pro objekty ukládané do kolekcí. Taktéž si řekneme, jakým způsobem se může programátor orientovat v rozsáhlých projektech a jak může zjistit, z kterých míst jsou volány vybrané metody atd.
Obrázek 18: Metody hashCode() a equals() vygenerované integrovaným vývojovým prostředím Eclipse.
3. 4. 2012 at 00:09
Pekne. Nicmene generovane (nebo rucne psane) gettery, settery, toString(), hashCode() a equals() vypadaji slozite. Navic se casto zapomene na jejich aktualizaci pri zmene fieldu. Napr. kdyz se prida novy field tak je potreba zmenit toString(), hashCode() a equals(). Nebo kdyz se treba prejmenuje field (ALT+SHIFT+R v elcipse) tak vznikne v toString() neco jako … + “ oldFieldName = “ + newFieldName + … Prejmenuje se field ale ten stary nazev uvnitr uvozovek se nezmeni.
Proto a nejen proto bcyh doporucil spis http://projectlombok.org
3. 4. 2012 at 11:37
Mate pravdu, nekdy je to problem (i kdyz Eclipse dost pomaha). K projektu Lombok se urcite jeste v tomto serialu dostanu 🙂
3. 4. 2012 at 19:34
Knihovna commons-lang od apache nabizi ToStringBuilder, EqualsBuilder a HashCodeBuilder ktere prochazi properties reflexi. Da se nastavit, aby je Eclipse generoval misto klasickych. viz. http://azagorneanu.blogspot.com/2011/08/how-to-generate-equals-hashcode.html