public static final int MONDAY = 0;
public static final int TUESDAY = 1;
public static final int WEDNESDAY = 2;
Tomáš Pitner, Radek Ošlejšek, Marek Šabo, Jakub Čecháček pitner@muni.cz
Chceme reprezentovat dny v týdnu.
public static final int MONDAY = 0;
public static final int TUESDAY = 1;
public static final int WEDNESDAY = 2;
Problémem je, že nemáme žádnou kontrolu:
typovou: metoda přijímající den má parametr typu int, takže bere libovolné číslo, třeba 2000
, a to nebude fungovat.
hodnotovou: dva dny v týdnu mohou omylem mít stejnou hodnotu a překladač nám to taky neodchytí.
enum
Typově bezpečná cesta, jak vyjmenovat a používat pojmenované konečné výčty prvků.
Proměnná určitého výčtového typu může pak nabývat vždy jedné hodnoty z daného výčtu.
Definice výčtového typu "den v týdnu":
public enum Day {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}
Zde bylo třeba podědit ze třídy Enum
a ručně ohodnotit jednotlivé prvky výčtu:
class State(Enum):
INIT = 1
RUNNING = 2
STOPPED = 3
Velmi příjemné použití ve větvení switch
public String tellItLikeItIs(Day day) {
switch (day) {
case MONDAY:
return "Mondays are bad.";
case FRIDAY:
return "Fridays are better.";
case SATURDAY, SUNDAY:
return "Weekends are best.";
default:
return "Midweek days are so-so.";
}
}
Klíčové slovo break
může být vynecháno, protože return
způsobí okamžitý návrat z funkce.
enum
Lze snadno a bezpečně testovat rovnost pomocí ==
:
day == MONDAY
day.equals(MONDAY)
- funguje stejně, pouze selže při day == null
MONDAY.equals(day)
- bezpečněji, ale stejně lépe psát day == MONDAY
Díky tomu, že každý výčet implementuje Comparable<E>
, lze prvky i řadit. Platí tedy, že MONDAY
je před TUESDAY
atd.
Výčtový typ se koncepčně velmi podobá třídě, de facto je to třída.
Výčet má však jen pevně daný počet prvků (instancí).
Pro každý prvek daného typu enum
je získatelné jeho ordinální číslo (pořadí) metodou int ordinal()
.
Každý námi definovaný výčtový typ je potomkem třídy java.lang.Enum.
Podobně jako jiné třídy má vestavěné metody a může mít další metody, konstruktory apod.
Využijeme, že enum
je de facto třída.
Přidáme atributy, případně metody nebo i konstruktor.
Dobře se využije možnost definovat jednotlivé prvky výčtu s inicializací jeho atributů.
Lze i překrýt metody jako toString
a vracet jiné textové reprezentace
Nelze však překrýt equals
, porovnání proto zůstává takové, že x.equals(y) <⇒ x == y
enum OrderStatus {
// 3 instances and no more ever
// they might by initialized with parameters
ORDERED(5), PREPARING(2), READY(0);
// enum may have attributes (not necessarily final)
int timeToReady;
OrderStatus(int timeToReady) {
this.timeToReady = timeToReady;
}
// may override toString
@Override public String toString() { return "ready in " + timeToReady; }
// this would not compile! must NOT override equals
@Override public boolean equals(Object o) {
if(o instanceof OrderStatus status)
return timeToReady == status.timeToReady;
return false;
}
}
OrderStatus status = OrderStatus.PREPARING;
System.out.println(status);
// may even directly modify status properties
OrderStatus.PREPARING.timeToReady = 1;
System.out.println(status);
// status is still == (though modified inside)
System.out.println(status == OrderStatus.PREPARING);
Toto se nezkompiluje, duplicitní identifikátor ORDERED
.
Že pokaždé jinak inicializovaný, nehraje roli.
Parametry v závorce nezpůsobí vytvoření nových prvků jako new
.
enum OrderStatus {
ORDERED(5), ORDERED(15), PREPARING(2), READY(0);
}
Jsou uspořádané dle pořadí, v jakém jsou uvedeny, aniž bychom cokoli definovali.
Nelze ovšem psát OrderStatus.ORDERED < OrderStatus.PREPARING
, ale je nutno využít compareTo
:
OrderStatus.ORDERED.compareTo(OrderStatus.PREPARING)
EnumSet
Jelikož prvků výčtu je konečný (a většinou nevelký) počet, je dobré, že pro ně máme speciální typ množiny - java.util.EnumSet
.
EnumSet
je abstraktní podtřídou AbstractSet
a má dvě neabstraktní implementace:
RegularEnumSet
vnitřně implementován jako bitové pole do velikosti 64 bitů, a to pomocí jednoho long
JumboEnumSet
jako bitový vektor libovolné, tzn. i větší velikosti
EnumSet
EnumSet<OrderStatus> set = EnumSet.of(OrderStatus.ORDERED, OrderStatus.READY);
// prints [ready in 5, ready in 0]:
System.out.println(set);
// prints [ready in 2]:
System.out.println(EnumSet.complementOf(set));
// prints class java.util.RegularEnumSet:
System.out.println(set.getClass());
další použitelné (statické) metody EnumSet:
EnumSet.noneOf()
→ vytvoří prázdnou množinu
** EnumSet.range(LOWER, UPPER)
→ vytvoří množinu prvků od LOWER po UPPER
EnumMap
Z obdobného důvodu se nabízí rovněž mapa EnumMap
.
Klíčem jsou prvky výčtu (tzn. jsou pevně předem dány a většinou jich není moc).
EnumMap<OrderStatus, Integer> countOrders
= new EnumMap<OrderStatus, Integer>(OrderStatus.class);
countOrders.put(OrderStatus.ORDERED, 4);
countOrders.put(OrderStatus.READY, 2);
System.out.println(countOrders);
Hezký příklad najdete na The Java™ Tutorials — Enum Types