Enum - Typ wyliczeniowy
Spis treści
Enum to typ wyliczeniowy, który umożliwia w Javie zadeklarowanie ograniczonej liczby możliwych wartości. Przydatny np. w przypadku deklaracji statusów procesu.
Prosty enum
Osobna klasa:
public enum Kolor {
CZERWONY, ZIELONY, NIEBIESKI;
}
Wewnętrzna klasa:
public class EnumTest {
public enum Kolor {
CZERWONY, ZIELONY, NIEBIESKI;
}
}
Zastosowanie:
public static void main(String[] args) {
EnumTest.Kolor kolor = EnumTest.Kolor.CZERWONY;
if(kolor.equals(EnumTest.Kolor.CZERWONY)) {
System.out.println("Zgadza sie");
}
if(kolor.equals(EnumTest.Kolor.NIEBIESKI)) {
System.out.println("Zgadza sie");
} else {
System.out.println("Nie zgadza sie");
}
}
Na razie szału nie ma, ale spokojnie - to dopiero początki.
Własny Konstruktor
public enum Kolor {
CZERWONY(false),
ZIELONY(true),
NIEBIESKI(true);
boolean ladny;
private Kolor(boolean czyLadny) {
ladny = czyLadny;
}
}
Przykładowe zastosowanie:
public class EnumTest {
public enum Kolor {
CZERWONY(false),
ZIELONY(true),
NIEBIESKI(true);
boolean ladny;
private Kolor(boolean czyLadny) {
ladny = czyLadny;
}
}
public static void main(String[] args) {
Kolor kolor = EnumTest.Kolor.CZERWONY;
System.out.println("Kolor czerwony jest "+czyLadny(kolor));
kolor = EnumTest.Kolor.ZIELONY;
System.out.println("Kolor zielony jest "+czyLadny(kolor));
}
public static String czyLadny(Kolor kolor) {
return (kolor.ladny) ? "ladny" : "brzydki";
}
}
Wynik:
Kolor czerwony jest brzydki
Kolor zielony jest ladny
Można też trochę zwięźlej:
public class EnumTest {
public enum Kolor {
CZERWONY(false),
ZIELONY(true),
NIEBIESKI(true);
boolean ladny;
private Kolor(boolean czyLadny) {
ladny = czyLadny;
}
}
public static void main(String[] args) {
System.out.println(czyLadny(Kolor.CZERWONY));
System.out.println(czyLadny(Kolor.ZIELONY));
}
public static String czyLadny(Kolor kolor) {
String czyLadny = (kolor.ladny) ? "ladny" : "brzydki";
return "Kolor "+kolor.toString()+" jest "+czyLadny;
}
}
Wynik:
Kolor CZERWONY jest brzydki
Kolor ZIELONY jest ladny
Tym razem wynik jest niezadowalający, ponieważ cała nazwa drukowana jest z dużych liter. Spróbujemy to naprawić
Przesłonięcie metody toString()
public enum Kolor {
CZERWONY(false),
ZIELONY(true),
NIEBIESKI(true);
boolean ladny;
private Kolor(boolean czyLadny) {
ladny = czyLadny;
}
@Override
public String toString() {
String poprzedniaNazwa = super.toString();
String nowaNazwa = poprzedniaNazwa.toLowerCase();
return nowaNazwa;
}
}
Krócej:
@Override
public String toString() {
return super.toString().toLowerCase();
}
No i wynik wywołania:
Kolor czerwony jest brzydki
Kolor zielony jest ladny
Iteracja po elementach
Możemy bezproblemowo przeiterować po elementach enuma.
Za pomocą zwykłej pętli for:
Kolor[] kolory = Kolor.values();
for(int i=0; i<kolory.length; i++) {
System.out.println(czyLadny(kolory[i]));
}
Krócej:
for(int i=0; i<Kolor.values().length; i++) {
System.out.println(czyLadny(Kolor.values()[i]));
}
Za pomocą foreach:
for(Kolor kolor: Kolor.values()) {
System.out.println(czyLadny(kolor));
}
Chyba nie muszę przekonywać która opcja wygląda najbardziej przejrzyście :)
Wynik wywołania:
Kolor czerwony jest brzydki
Kolor zielony jest ladny
Kolor niebieski jest ladny
Implementowanie interfejsów
Enum, jak reszta klas w Javie, może rozszerzać interfejsy. Przykład:
public class EnumTest {
public enum Kolor implements Runnable {
CZERWONY(false),
ZIELONY(true),
NIEBIESKI(true);
boolean ladny;
private Kolor(boolean czyLadny) {
ladny = czyLadny;
}
@Override
public String toString() {
String poprzedniaNazwa = super.toString();
String nowaNazwa = poprzedniaNazwa.toLowerCase();
return nowaNazwa;
}
@Override
public void run() {
System.out.println(czyLadny(this));
}
}
public static void main(String[] args) {
for(Runnable kolor: Kolor.values()) {
kolor.run();
}
}
public static String czyLadny(Kolor kolor) {
String czyLadny = (kolor.ladny) ? "ladny" : "brzydki";
return "Kolor "+kolor.toString()+" jest "+czyLadny;
}
}
Dowodem na to, że wszystko działa może być linia:
for(Runnable kolor: Kolor.values()) {
Własny komparator
Typ enum nie umożliwia nadpisania metody int compareTo() - jest ona ustawiona jako finalna. Co zrobić w takim razie, jeśli potrzebujemy własny komparator?
Piszemy własną klasę implementującą interfejs Comparator
public class MojKomparator implements Comparator<Kolor> {
public int compare(Kolor pierwszy, Kolor drugi) {
// najpierw porównaj, czy obydwa sa ladne / brzydkie
if (pierwszy.ladny != drugi.ladny) {
if (pierwszy.ladny) {
return -1;
} else {
return 1;
}
} else {
// jesli te same, to porównaj nazwy (alfabetycznie)
return pierwszy.toString().compareTo(drugi.toString());
}
}
}
Następnie jej używamy:
public static void main(String[] args) {
EnumTest test = new EnumTest();
MojKomparator mojKomparator = test.new MojKomparator();
Kolor[] kolory = Kolor.values();
System.out.println("Przed sortowaniem:");
for (Kolor kolor : kolory) {
System.out.println(kolor.toString());
}
Arrays.sort(kolory, mojKomparator);
System.out.println("\nPo sortowaniu");
for (Kolor kolor : kolory) {
System.out.println(kolor.toString());
}
}
Wynik:
Przed sortowaniem:
czerwony
zielony
niebieski
Po sortowaniu (najpierw ładne, potem brzydkie, a jeśli oba są tej samej kategorii, to sortujemy alfabetycznie po nazwie)
niebieski
zielony
czerwony
Użyteczny przykład użycia
Załóżmy, że piszemy program i potrzebna jest funkcjonalność odczytania od użytkownika odpowiedzi typu tak lub nie.
Pierwsze rozwiązanie jakie przychodzi na myśl, to po prostu porównanie tekstu podanego przez usera. Mniej więcej coś takiego:
public boolean odpUsera(String wejscie) {
if(wejscie.equals("tak"))
return true;
return false;
}
Jednak co się stanie, jeśli użytkownik wpisze "t" albo "yes". Tego nie przewidujemy. Można oczywiście dodać dodatkowe warunki, ale czy taki kod:
public boolean odpUsera(String wejscie) {
if (wejscie.equals("tak") || wejscie.equals("t")
|| wejscie.equals("yes") || wejscie.equals("y")
|| wejscie.equals("sure") || wejscie.equals("ok"))
return true;
return false;
}
wygląda elegancko? Według mnie niezbyt. Spróbujemy zrealizować to stosując odpowiedni enum.
public class EnumPrzyklad {
public enum Odpowiedz {
TAK(true),
NIE(false),
T(true),
N(false),
YES(true),
NO(false),
OK(true),
SURE(true),
NOPE(false);
private boolean wartosc;
private Odpowiedz(boolean wartosc) {
this.wartosc = wartosc;
}
public boolean getWartosc() {
return wartosc;
}
}
public static void main(String[] args) {
EnumPrzyklad test = new EnumPrzyklad();
String[] wartosciUsera = {"tak", "TAK", "OK", "ta", "nie", "niewiem", "NoPe" };
test.simplePrint("==WEJSCIE==", "==WYJSCIE==");
test.simplePrint("-----------", "-----------");
for(String wejscie : wartosciUsera) {
test.simplePrint(wejscie, test.odpUseraString(wejscie));
}
}
public String odpUseraString(String wejscie) {
try {
return odpUsera(wejscie) ? "pozytywna" : "negatywna";
} catch (UnknownAnswerException e) {
return "nieznana";
}
}
public boolean odpUsera(String wejscie) throws UnknownAnswerException {
for(Odpowiedz odp : Odpowiedz.values()) {
if(odp.toString().equalsIgnoreCase(wejscie))
return odp.getWartosc();
}
throw new UnknownAnswerException();
}
private void simplePrint(String one, String two) {
System.out.printf("%15s | %10s\n", one, two);
}
}
Wynik:
==WEJSCIE== | ==WYJSCIE==
----------- | -----------
tak | pozytywna
TAK | pozytywna
OK | pozytywna
ta | nieznana
nie | negatywna
niewiem | nieznana
NoPe | negatywna
Podsumowanie
Jak widać enum ma sporo zastosowań. Najczęściej korzysta się z niego jednak jako przedstawienie wszystkich dostępnych wartości i tam sprawdza się najlepiej.
Dyskusja i komentarze
Masz pytania do tego wpisu? Może chcesz się podzielić spostrzeżeniami? Zapraszamy dyskusji na naszej grupie na Facebooku.
Poniżej znajdziesz archiwalne wpisy z czasów, gdy strona była jeszcze hobbystycznym blogiem.
Emihis
Dzieki! Te informacje napewno sie przydadza:)
Marcinho
Bardzo pomocny artykuł :)
Rob
W przykładzie z sotrowaniem, nie posotrowało alfabetycznie.
Marcin Kunert
Faktycznie, zaraz sprawdzę o co chodzi. Dzięki za info.
Marcin Kunert
Sprawdziłem - jednak jest dobrze. Sortujemy najpierw po kategorii, a potem alfabetycznie. (najpierw ładne, potem brzydkie, a jeśli oba są tej samej kategorii, to sortujemy alfabetycznie po nazwie)
domek
Jest źle. Błąd polega na tym, że komparator przy dwóch różnych wartościach (bez wzglęgu czy dostaje true, false czy false, true) zawsze uznaje, że pierwszy powinien być pierwszy argument. Należałoby jeszcze sprawdzić czy pierwszy argument to true i zwrócić -1, jeśli false zwrócić 1. W powyższym przypadku sortuje akurat dobrze, ale wystarczy dodać jako pierwszy jakiś dodatkowy kolor będący true, np. BIALY(true), ZOLTY(true), CZERWONY(false), ZIELONY(true), NIEBIESKI(true); I już sortowanie będzie błędne (w podanym przypadku CZERWONY będzie pierwszy, mimo, że jest false).
Marcin Kunert
Masz rację. Dzięki za wskazanie błędu. Poprawiłem.
Bartol
Podswietla mi UnknownAnswerException, dlaczego ?!
Marcin Kunert
Bo musisz taki wyjątek stworzyć. public class UnknownAnswerException extends Exception { }
Bartol
Dobra, pomogło. Dzięki.