==== Výčtový typ - Enum ==== Pro účely karetních her zkusme porovnat reprezentaci barev karet pomocí konstant a pomocí enum. Všechny úkoly zkuste nejprve pomocí konstant: - Vytvořte třídu karta, která bude mít konstruktor obsahující hodnotu a barvu karty, - bude obsahovat metodu toString, která textově vypíše hodnotu a barvu karty. A nyní pomocí ENUM, pro načtení hodnot můžete použít static import. Napište metody pro výpis a výpis v angličtině (diamonds (♦), spades (♠), hearts (♥) and clubs (♣)). Kde jsou nebezpečí používání konstant? import java.util.*; public class Card { public enum Rank { DEUCE, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, JACK, QUEEN, KING, ACE } public enum Suit { CLUBS, DIAMONDS, HEARTS, SPADES } private final Rank rank; private final Suit suit; private Card(Rank rank, Suit suit) { this.rank = rank; this.suit = suit; } public Rank rank() { return rank; } public Suit suit() { return suit; } public String toString() { return rank + " of " + suit; } private static final List protoDeck = new ArrayList(); // Initialize prototype deck static { for (Suit suit : Suit.values()) for (Rank rank : Rank.values()) protoDeck.add(new Card(rank, suit)); } public static ArrayList newDeck() { return new ArrayList(protoDeck); // Return copy of prototype deck } } ==== Enum jak ho neznáme ==== Řekněme, že chceme pracovat s elementy, které je možné řadit vzestupně nebo sestupně. Mějme následující výchozí Enum: public enum Ordering { ASCENDING, DESCENDING; } === Atributy === Enum může mít atributy, stejně jako jakákoliv jiná třída. Tyto atributy lze nastavit v konstruktoru, který lze definovat. Deklarace jednotlivých prvků jsou vlastně volání konstruktoru (akorát když žádný není, vynecháme závorky) a lze tak předat atributům hodnoty. Uložme si pro každé seřazení jeho zkratku: public enum Ordering { ASCENDING("ASC"), DESCENDING("DESC"); public final String acronym; private Ordering(String acronym) { this.acronym = acronym; } } Atribut můžete použít stejně jako u jakékoli jiné třídy, např.: Ordering[] ords = Ordering.values(); for (Ordering o : ords) { System.out.println(o.acronym + " - " + o); } Produkuje: ASC - ASCENDING DESC - DESCENDING === Metody === Enum může mít metody, stejně jako jakákoliv jiná třída. A jelikož i Enum je potomkem třídy Object (stejně jako všechny třídy v javě), ukažme to na metodě ''toString()'': public enum Ordering { ASCENDING("ASC"), DESCENDING("DESC"); public final String acronym; private Ordering(String acronym) { this.acronym = acronym; } @Override public String toString() { return this.name() + "(" + acronym + ")"; } } Následující kód for (Ordering o : Ordering.values()) { System.out.println(o); } pak produkuje ASCENDING(ASC) DESCENDING(DESC) === Rozhraní === Stejně jako jakákoliv jiná třída, i Enum může implementovat rozhraní (interface). Implementujme na našem ''Ordering'' rozhraní ''java.util.Comparator'' pro ''Integer''. Máme dvě možnosti. == 1. Speciální implementace pro každý prvek == Každý prvek si implementuje vlastní verze metod rozhraní: public enum Ordering implements Comparator { ASCENDING { @Override public int compare(Integer o1, Integer o2) { return o1.compareTo(o2); } }, DESCENDING { @Override public int compare(Integer o1, Integer o2) { return o2.compareTo(o1); } }; } Ukázkový kód: List elementsBase = Arrays.asList(3, 1, 40, 42, 42, 51, -134, 0, 0); List sortedAsc = new ArrayList(elementsBase); Collections.sort(sortedAsc, Ordering.ASCENDING); List sortedDesc = new ArrayList(elementsBase); Collections.sort(sortedDesc, Ordering.DESCENDING); System.out.println(sortedAsc); System.out.println(sortedDesc); Produkuje následující výstup: ascending: [-134, 0, 0, 1, 3, 40, 42, 42, 51] descending: [51, 42, 42, 40, 3, 1, 0, 0, -134] == 2. Jediná společná implementace s vnitřním rozhodováním na základě ''this'' == Metody rozhraní jsou implementovány pouze jednou, na stejné úrovni jako např. konstruktor a až uvnitř nich se rozhodneme, co budeme dělat na základě toho, co je vlastně ''this'': public enum Ordering implements Comparator { ASCENDING, DESCENDING; @Override public int compare(Integer o1, Integer o2) { switch (this) { case ASCENDING: return o1.compareTo(o2); case DESCENDING: return o2.compareTo(o1); } throw new IllegalStateException(); // we can never reach this code but without it // compiler complains that the method might not // return anything } } Chová se totožně jako předchozí varianta. Nicméně, **tento postup se nedoporučuje.** Navíc ani není moc hezký (nedosažitelná výjimka na konci) a není to správně objektové. === Ještě jedna poznámka k metodám === Metody samotné lze také implementovat pro každý prvek zvlášť, jako jsme viděli u rozhraní. Náš ''toString()'' by tedy mohl vypadat i takto: public enum Ordering { ASCENDING { @Override public String toString() { return this.name() + "(ASC)"; } }, DESCENDING { @Override public String toString() { return this.name() + "(DESC)"; } }; } Kterou ze dvou variant použít, je otázka. Obecné pravidlo by se dalo vyložit asi takto: pokud je chování metody specifické pro každý prvek (dá se zhruba říct i jako "bylo by třeba dělat ''switch(this)''"), je vhodné ji implementovat pro každý prvek zvlášť. Pokud je chování pro všechny prvky víceméně shodné (či "není třeba dělat ''switch(this)''"), je vhodné ji implementovat na úrovni Enumu. === Další studium === Pro maximální porozumění (nejen) Enumům doporučuji Chapter 6, Items 30-34 z knihy Effective Java, 2nd Edition.