Zatím jsme si v tomto seriálu představili konstrukty třídy (class) a rozhraní (interface). Dnes je doplníme o výčtový typ (enum), čímž tuto tématiku zakončíme.
Co je to enum?
Výčtový typ enum, který byl přidán do Javy ve verzi 5, nám umožňuje vytvářet vlastní datové typy, které mohou nabývat pouze určitého omezeného množství hodnot. Každé z těchto hodnot poté odpovídá právě jedna instance výčtu.
Příkladem mohou být měsíce v roce (leden, únor...), dny v týdnu (pondělí, úterý...) nebo světové strany (sever, jih...).
Výhody výčtového typu
Dokud v Javě nebyl výčtový typ implementován, tak byl programátory emulovám pomocí rozhraní obsahujícího odpovídající množinu (obvykle celočíselných) konstant. Tento postup měl několik nedostatků.
Prvním a nejvýznamnějším nedostatkem byla typová bezpečnost. Pokud chtěl programátor předat metodě konstantu z takto emulovaného výčtu, tak musel mít v hlavičce metody uveden celý její rozsah (tj. metoda přijímala například typ int). Takto definovaná metoda ovšem nebyla jakkoliv odolná vůči volání s nesprávnou/nedefinovanou hodnotou parametru (například při změně implementace).
Druhým nedostatkem je samotná neobjektovost tohoto přístupu. Konstanta definovaná v rámci rozhraní s sebou nemůže nést žádné chování, které tudíž musí být implementováno někde jinde, nejčastěji v metodě, která tuto konstantu přijímá ve svém parametru.
Další nevýhodou je nízká informační hodnota předané konstanty, která se projeví především při logování a debugování programu. Představme s situaci, kdy předáme konstantu API vysoké úrovně, které ji postupně posílá do nižších vrstev aplikace. Pokud dojde k chybě v některém z těchto volání, tak se patrně pouze dozvíme, že došel chybný parametr "5", ale nikoliv, co vlastně znamená (pro převod bychom museli využít nějakého číselníku).
Výčtový typ enum všecny tyto nedostatky zcela eliminuje.
Deklarace
Deklarace výčtového typu je velmi podobná deklaraci třídy. Pouze v hlavičce nepoužijeme klíčové slovo class, ale slovo enum. Dále v rámci těla musíme nejprve vyjmenovat všechny možné hodnoty (instance), kterých může výčet nabývat. Ve zbytku těla smíme použít stejné konstrukty jako u libovolné třídy.
Omezení
Jak jsme si již uvedli, enum může nabývat pouze omezeného množství hodnot (reprezentovaných vyjmenovanými instancemi). Z toho plyne, že konstruktor enumu je vždy soukromý a enum jako takový je konečný (final) – což znamená, že z něj nelze odvozovat podtypy.
Jelikož jsou výčty serializovatelné, tak Java zaručuje, že v případě deserializace objektu výčtového typu nevznikne nová instance.
Z těchto omezení pro nás plyne také jedna úleva. Pro porovnávání hodnot výčtového typu můžeme bez obav používat operátor == (není třeba volat metodu equals).
Společný předek – třída Enum
Společným předkem všech výčtových typů je třída Enum (dokumentace), která definuje základní společné metody a implementuje rozhraní Serializable a Comparable.
Ze společných operací uveďme metody values a ordidal. Metoda values vrací pole všech instancí výčtu a je zajímavá tím, že ji přidává až překladač během kompilace. Metodu ordinal vrací pořadí, v němž byla daná instance v rámci výčtu nadefinována (indexováno od nuly).
Příklad
Vytvořme si výčtový typ pro měsíce v roce, který obsahovat metodu getRandom, která vrátí náhodný měsíc.
/** * Vyctovy typ pro mesice v roce * @author Pavel Micka */ public enum Month { JANUARY, FEBRUARY, MARCH, APRIL, MAY, JUNE, JULY, AUGUST, SEPTEMBER, OCTOBER, NOVEMBER, DECEMBER; /** * Vrati nahodny mesic * @return nahodny mesic */ public static Month getRandom(){ Random r = new Random(); // pseudonahodny generator cisel int count = Month.values().length; return Month.values()[r.nextInt(count)]; } }
Instance jednotlivých měsíců nyní zíkáme následovně:
Month m1 = Month.JANUARY; // Ziskejme instanci odpovidajici lednu Month m2 = Month.getRandom(); // Ziskejme nahodnou instanci
Konstruktor
Rozšiřme nyní náš příklad a definujme konstruktor, jenž předá jednotlivým měsícům pranostiky, které se k nim vztahují.
/** * Vyctovy typ pro mesice v roce * @author Pavel Micka */ public enum Month { JANUARY("Je-li teplo v lednu, sahá bída ke dnu."), FEBRUARY("Únor bílý, pole sílí."), MARCH("Březen - za kamna vlezem."), APRIL("Duben - ještě tam budem."), MAY("Máj - vyženem kozy v háj."), JUNE("Když kvete chrpa, za čtyři neděle chop se srpa."), JULY("Co červenec neuvaří - srpen nedopeče."), AUGUST("Když srpen z počátku hřeje, ledový vítr v zimě dlouho věje."), SEPTEMBER("Bouřka v září - sníh v prosinci."), OCTOBER("Sněží-li v říjnu, bude měkká zima."), NOVEMBER("Když začátkem listopadu sněží, pak mívá sníh výšku věží."), DECEMBER("Chodí-li Kateřina po ledě, chodí Štěpán po blátě."); private String weatherLore; private Month(String weatherLore) { this.weatherLore = weatherLore; } /** * Vrati nahodny mesic * @return nahodny mesic */ public static Month getRandom(){ Random r = new Random(); // pseudonahodny generator cisel int count = Month.values().length; return Month.values()[r.nextInt(count)]; } /** * Vrati pranostiku na dany mesic * @return */ public String getWeatherLore() { return weatherLore; } }
Enum jako přepínač
Významnou vlastností výčtu je, že jej můžeme použít jako proměnnou v přepínačí (switch).
Month m = Month.getRandom(); switch(m){ case JUNE : System.out.println("Hura na prazdniny!"); break; case SEPTEMBER: System.out.println("Skola je tu. :-("); break; case DECEMBER: System.out.println("Budou Vanoce!"); break; default: System.out.println("V tomto mesici se nic zvlastniho nedeje. :-)"); System.out.println(m.name()); //vypiseme nazev mesice (nazev instance) break; }
Litaratura
- VIRIUS, Miroslav. Java 5. Praha : Neocortex, spol s r.o., 2005. Java 5, s. 1349-1411. ISBN 80-86330-12-5.