Kurs Java

Typ wyliczeniowy enum

Enum to typ wyliczeniowy (z ang. enumerated type). Wygląda jak klasa, tyle że zamiast słowa class piszemy enum. Enum zwykle zawiera listę nazwanych stałych ("zwykle", bo może mieć na przykład tylko jedną stałą). Podstawową różnicą w stosunku do klas jest to, że instancja stałej jest tworzona NIE przez użycie słowa kluczowego new, ale przez zwykłe odwołanie się do stałej.
Appa Notka. Technicznie rzecz ujmując, można powiedzieć, że jeśli na bazie klas powstają obiekty, to owe instancje stałych też są obiektami (enumy mają konstruktory, pola i metody, a nawet mogą implementować interfejsy). Natomiast w oficjalnej dokumentacji Javy (co ciekawe), nie są one jawnie nazwane obiektami. Co prawda znajdują się w sekcji przedstawiającej klasy i obiekty, ale w konkretnym rozdziale Enum Types nie są tak nazywane (dokumentacja mówi o nich po prostu jako o stałych). Niemniej ze względu na to, iż wywodzą się z klas i posiadają większość ich cech, określenia obiekt będziemy używać umownie.
Na przykład w aplikacji przechowującej dokumenty, możemy za pomocą enuma określić nazwy typów dokumentów. W ten sposób ustalamy, że tylko takie wartości mogą zostać zdefiniowane jako typ dokumentu.
public enum DocumentType {

    INVOICE, CONTRACT, CERTIFICATE, NOTARIAL_ACT;

    // Pola

    // Konstruktory

    // Metody
}
Do takiego enuma odwołamy się w ten sposób:
public class Start {

    public static void main(String[] args) {

        Enum contract = DocumentType.CONTRACT;
        Enum invoice = DocumentType.INVOICE;
        Enum notarialAct = DocumentType.NOTARIAL_ACT;
        Enum certificate = DocumentType.CERTIFICATE;

        checkEnum(contract);
        checkEnum(invoice);
        checkEnum(notarialAct);
        checkEnum(certificate);
    }

    static void checkEnum(Enum documentType) {

        if(documentType.equals(DocumentType.CERTIFICATE)) {
            System.out.println(documentType);
        }
    }

}
Wynik wykonania kodu:
Java 8 - Data i czas przed Javą 8
Na powyższym przykładzie dobrze widać, że typ Enum zachowuje się podobnie jak klasa. Jednak w przeciwieństwie do klas, obiektu nie tworzymy tutaj przez użycie słowa kluczowego new (może to jest powód unikania określenia obiekt w dokumentacji), tylko przez odwołanie się do stałej:
    Enum invoice = DocumentType.INVOICE;
W taki sposób powstaje obiekt typu Enum (a w naszym przykładzie także typu DocumentType). Możemy przekazać go jako parametr metody. Tak samo możemy wykonać porównanie za pomocą metody equals. W tym momencie warto sobie odpowiedzieć na pytanie, z czego właściwie wynika to podobieństwo dla klas?

Skąd się bierze enum ?

Enum działa podobnie jak klasa, bo i też wywodzi się z klasy. Precyzyjniej mówiąc, wywodzi się (dziedziczy niejawnie) z klasy abstrakcyjnej o nazwie Enum:
public abstract class Enum<E extends Enum<E>> implements Comparable<E>, Serializable {

    private final String name;

    ...
    
    public final String name() {
        return name;
    }

    ...

    public String toString() {
        return name;
    }

    public final boolean equals(Object other) {...}

    public final int hashCode() {...}

    public final int compareTo(E o) {...}

    public static <T extends Enum<T>> T valueOf(Class<T> enumType, String name) {...}

    ...
}
Tak więc enum może mieć konstruktory, metody i pola. Istotne jest jednak to, że programista nie używa konstruktora do stworzenia enuma. Tworzenie odbywa się w sposób pokazany wcześniej, czyli przez odwołanie się do stałej. Ale to już wiemy. To, co jeszcze jest ważne, to charakterystyka tej stałej. Stała jest publiczna, statyczna oraz finalna.

Metoda values

Metoda values umożliwia zwrócenie wszystkich wartości danego enuma. Może nie jest idealna (zwraca tablicę), ale za to przydaje się, gdy chcemy wykonać jakieś operacje kolejno dla każdej z wartości:
public class Start {

    public static void main(String[] args) {

        DocumentType[] documentTypes = DocumentType.values();
        for (DocumentType documentType : documentTypes) {
            System.out.println(documentType);
        }
    }
}
Wynik wykonania kodu:
Java - Enum lista wartości

Metoda valueOf

Metoda valueOf zwraca stałą z enuma dla wartości podanej w parametrze:
public class Start {

    public static void main(String[] args) {

        Enum contract = DocumentType.valueOf("CONTRACT");
        System.out.println(contract);
        
        DocumentType invoice = DocumentType.valueOf("INVOICE");
        System.out.println(invoice);        
    }
}
Wynik wykonania kodu:
Java - Enum lista wartości
Tutaj dobrze widać to, że obiekty są zarówno typu Enum, jak i DocumentType.

Konstruktor i metody

Teraz pytanie, co będzie, jeśli do naszego enuma dodamy pole, które ustawimy z poziomu konstruktora?
public enum DocumentType {

    INVOICE, CONTRACT, CERTIFICATE, NOTARIAL_ACT;

    private int id;

    DocumentType(int id) {
        this.id = id;
    }
}
W takiej sytuacji nasz enum przestanie się kompilować (żółty kolor na zdjęciu wynika z podpowiedzi IntelliJ - można go tutaj pominąć):
Java - Kurs Java
Dzieje się tak, ponieważ wprowadzając konstruktor parametrowy, wyłączyliśmy widoczność konstruktora domyślnego (bezparametrowego). Można powiedzieć, że przykład ze zdjęcia doskonale obrazuje to, jak działają enumy. Widać, że samo zdefiniowanie wartości jest rozumiane jako chęć stworzenia obiektu. To trochę tak, jakbyśmy tam mieli napisane new INVOICE(), new CONTRACT() itd. Tylko że tak wygląda tworzenie obiektów z regularnych klas. W przypadku enumów wystarczy samo zdefiniowanie stałej wartości.

Tak więc, skoro mamy tutaj do czynienia z konstruktorem parametrowym, to nie pozostaje nam nic innego, jak podać wartość tego parametru. Jako że parametr określiliśmy w postaci typu int, przekazujemy wartość w postaci liczby.
public enum DocumentType {

    INVOICE(1), CONTRACT(2), CERTIFICATE(3), NOTARIAL_ACT(4);

    private int id;

    DocumentType(int id) {
        this.id = id;
    }

    public int getId() {
        return id;
    }
}
Metodę getId wywołujemy normalnie tak, jak na regularnym obiekcie:
public class Start {

    public static void main(String[] args) {

        // Wywołanie metody getId na enumie CERTIFICATE:
        System.out.println("Id of CERTIFICATE enum:");
        System.out.println(DocumentType.CERTIFICATE.getId());
    }
}
Wynik wykonania kodu:
Java - Enum lista wartości

Wiele konstruktorów i wiele metod

Oczywiście, jeśli tylko potrzebujemy, możemy stworzyć kilka konstruktorów z różnymi parametrami (tak jak w przypadku typowych klas). Poniższy przykład przedstawia kilka różnych możliwości:
public enum DocumentType {

    INVOICE, CONTRACT(2, "agreement"), CERTIFICATE, NOTARIAL_ACT(4);

    private int id;
    private String alternativeName;

    DocumentType() {}

    DocumentType(int id) {
        this.id = id;
    }

    DocumentType(int id, String alternativeName) {
        this.id = id;
        this.alternativeName = alternativeName;
    }

    public int getId() {
        return id;
    }

    public String getAlternativeName() {
        return alternativeName;
    }
}
Metody - ponownie - wywołujemy normalnie, jak na każdym obiekcie:
public class Start {

    public static void main(String[] args) {
        
        // Wywołanie metody getId na enumie CONTRACT:
        System.out.println("Id of CONTRACT enum:");
        System.out.println(DocumentType.CONTRACT.getId());
        
        // Wywołanie metody getAlternativeName na enumie CONTRACT:
        System.out.println("Alternative name of CONTRACT enum:");
        System.out.println(DocumentType.CONTRACT.getAlternativeName());
    }
}
Wynik wykonania kodu:
Java - Enum lista wartości
Powyższe przykłady (szczególnie ostatni) bardzo dobrze obrazują ideę enumów. Definiując stałe, tak naprawdę od razu mówimy kompilatorowi, jakie obiekty ma stworzyć (stąd kompilator buntuje się, jeśli nie dostarczymy wymaganego konstruktora). W momencie, gdy odwołujemy się do stałej, obiekt zostaje stworzony i jest on dokładnie tego typu co nasz enum.

Enum i instrukcja switch

Na koniec przykład, w jaki sposób można użyć enuma w ramach instrukcji switch:
public class Start {

    public static void main(String[] args) {

        DocumentType selectedDocumentType = DocumentType.NOTARIAL_ACT;
        switch(selectedDocumentType) {
            case CONTRACT:
                System.out.println("You selected contract");
                break;
            case INVOICE:
                System.out.println("You selected invoioce");
                break;
            case NOTARIAL_ACT:
                System.out.println("You selected notarial act");
                break;
            case CERTIFICATE:
                System.out.println("You selected certificate");
                break;
        }
    }
}
Wynik wykonania kodu:
Java - Enum lista wartości
Masz pytanie dotyczące tego rozdziału? Zadaj je nam!
Masz pytanie dotyczące prezentowanego materiału?
Coś jest dla Ciebie niejasne i Twoje wątpliwości przeszkadzają Ci w pełnym zrozumieniu treści?
Napisz do nas maila, a my chętnie znajdziemy odpowiednie rozwiązanie.
Najciekawsze pytania wraz z odpowiedziami będziemy publikować pod rozdziałem.
Nie czekaj. Naucz się programować jeszcze lepiej.
kursjava@javappa.com

Stale się rozwijamy, a więc bądź na bieżąco!
Na ten adres będziemy przesyłać informacje o ważniejszych aktualizacjach, a także o nowych materiałach pojawiających się na stronie.
Polub nas na Facebooku:
Nasi partnerzy: stackshare
Javappa to również profesjonalne usługi programistyczne oparte o technologie JAVA. Jeśli chesz nawiązać z nami kontakt w celu uzyskania doradztwa bądź stworzenia aplikacji webowej powinieneś poznać nasze doświadczenia.
Kliknij O nas .