Co to są wyliczenia i dlaczego są przydatne?


488

Dzisiaj przeglądałem kilka pytań na tej stronie i znalazłem wzmiankę o enum zastosowaniu w singletonowym schemacie na temat rzekomych korzyści bezpieczeństwa takiego rozwiązania.

Nigdy nie używałem enums i programuję w Javie od ponad kilku lat. I najwyraźniej bardzo się zmienili. Teraz nawet wykonują pełne wsparcie OOP w sobie.

Dlaczego i po co mam używać enum w codziennym programowaniu?



możliwy duplikat Enum w Javie. Zalety?
finnw

Nie można utworzyć wystąpienia enums, ponieważ ma on prywatnego konstruktora, są one tworzone przy starcie jvm, więc jego singleton Enums nie jest bezpieczny dla wątków.
Arnav Joshi

Odpowiedzi:


633

Powinieneś zawsze używać wyliczeń, gdy zmienna (szczególnie parametr metody) może pobrać tylko jedną z niewielkiego zestawu możliwych wartości. Przykładami mogą być na przykład stałe typu (status kontraktu: „stały”, „temp”, „uczeń”) lub flagi („wykonaj teraz”, „odrocz wykonanie”).

Jeśli użyjesz wyliczeń zamiast liczb całkowitych (lub kodów łańcuchowych), zwiększysz sprawdzanie czasu kompilacji i unikniesz błędów w przekazywaniu niepoprawnych stałych oraz udokumentujesz, które wartości są dozwolone w użyciu.

BTW, nadużywanie wyliczeń może oznaczać, że twoje metody robią za dużo (często lepiej jest mieć kilka oddzielnych metod, niż jedną metodę, która pobiera kilka flag, które modyfikują to, co robi), ale jeśli musisz użyć flag lub kodów typów, wyliczenia są właściwą drogą.

Jako przykład, który jest lepszy?

/** Counts number of foobangs.
 * @param type Type of foobangs to count. Can be 1=green foobangs,
 * 2=wrinkled foobangs, 3=sweet foobangs, 0=all types.
 * @return number of foobangs of type
 */
public int countFoobangs(int type)

przeciw

/** Types of foobangs. */
public enum FB_TYPE {
 GREEN, WRINKLED, SWEET, 
 /** special type for all types combined */
 ALL;
}

/** Counts number of foobangs.
 * @param type Type of foobangs to count
 * @return number of foobangs of type
 */
public int countFoobangs(FB_TYPE type)

Wywołanie metody takie jak:

int sweetFoobangCount = countFoobangs(3);

następnie staje się:

int sweetFoobangCount = countFoobangs(FB_TYPE.SWEET);

W drugim przykładzie natychmiast wiadomo, które typy są dozwolone, dokumenty i implementacja nie mogą się nie zsynchronizować, a kompilator może to wymusić. Ponadto nieprawidłowe połączenie, takie jak

int sweetFoobangCount = countFoobangs(99);

nie jest już możliwe.


41
Jeśli używasz wyliczeń i chcesz zezwolić na kombinację wartości, użyj EnumSet. Jest dostarczany z wszelkiego rodzaju zgrabnymi pomocnikami, takimi jak publiczny statyczny końcowy EnumSet <FB_TYPE> ALL = EnumSet.allOf (FB_TYPE.class);
RobAu,

20
Te odpowiedzi bardzo lubię widzieć w SO, ponieważ ktoś włożył w to naprawdę dużo wysiłku, dobra robota, rozumiem teraz wyliczenia!
Dediqated

1
Nie sądzę, że to prawda you should *always* use enums when a variable can only take one out of a small set of possible values. Używanie stałych wartości jako „flag binarnych” (z logicznym orprzetwarzaniem) może być przydatne w aplikacjach czasu rzeczywistego, w których brakuje pamięci.
Elist

@Elist używając wyliczeń zwiększa czytelność twoich kodów, jeśli użyjesz stałych przez pewien czas ty lub twój następca nie wie, dlaczego jest używany ...
:)

136

Dlaczego warto korzystać z funkcji języka programowania? Powód, dla którego w ogóle mamy języki

  1. Programiści do wydajnego i prawidłowego wyrażania algorytmów w formie, w której mogą używać komputery.
  2. Opiekunowie, aby zrozumieć algorytmy, które inni napisali i poprawnie wprowadzają zmiany.

Wyliczenia zwiększają zarówno prawdopodobieństwo poprawności, jak i czytelności, bez pisania dużej liczby szablonów. Jeśli chcesz napisać bojler, możesz „symulować” wyliczenia:

public class Color {
    private Color() {} // Prevent others from making colors.
    public static final Color RED = new Color();
    public static final Color AMBER = new Color();
    public static final Color GREEN = new Color();
}

Teraz możesz napisać:

Color trafficLightColor = Color.RED;

Powyższa płyta ma podobny efekt jak

public enum Color { RED, AMBER, GREEN };

Oba zapewniają ten sam poziom sprawdzania pomocy kompilatora. Boilerplate to po prostu więcej pisania. Ale zaoszczędzenie dużej liczby pisania sprawia, że ​​programista jest bardziej wydajny (patrz 1), więc jest to przydatna funkcja.

Warto jest z co najmniej jeszcze jednego powodu:

Instrukcje przełączania

Jedną z rzeczy, których nie daje static finalpowyższa symulacja wyliczania , są ładne przypadki. W przypadku typów wyliczeniowych przełącznik Java używa typu swojej zmiennej, aby wywnioskować zakres przypadków wyliczania, więc w powyższym przypadku wystarczy powiedzieć:switchenum Color

Color color = ... ;
switch (color) {
    case RED:
        ...
        break;
}

Pamiętaj, że nie ma tego Color.REDw przypadkach. Jeśli nie używasz wyliczenia, jedynym sposobem użycia nazwanych ilości switchjest coś takiego:

public Class Color {
    public static final int RED = 0;
    public static final int AMBER = 1;
    public static final int GREEN = 2;
}

Ale teraz zmienna do przechowywania koloru musi mieć typ int. Ładne sprawdzanie wyliczenia i static finalsymulacji przez kompilator zniknęło. Nieszczęśliwy.

Kompromisem jest użycie elementu symulacji o wartości skalarnej:

public class Color {
    public static final int RED_TAG = 1;
    public static final int AMBER_TAG = 2;
    public static final int GREEN_TAG = 3;

    public final int tag;

    private Color(int tag) { this.tag = tag; } 
    public static final Color RED = new Color(RED_TAG);
    public static final Color AMBER = new Color(AMBER_TAG);
    public static final Color GREEN = new Color(GREEN_TAG);
}

Teraz:

Color color = ... ;
switch (color.tag) {
    case Color.RED_TAG:
        ...
        break;
}

Ale uwaga, jeszcze więcej płyt grzewczych!

Używanie wyliczenia jako singletonu

Z powyższej płyty kotłowej możesz zobaczyć, dlaczego enum zapewnia sposób na wdrożenie singletonu. Zamiast pisać:

public class SingletonClass {
    public static final void INSTANCE = new SingletonClass();
    private SingletonClass() {}

    // all the methods and instance data for the class here
}

a następnie dostęp do niego za pomocą

SingletonClass.INSTANCE

możemy po prostu powiedzieć

public enum SingletonClass {
    INSTANCE;

    // all the methods and instance data for the class here
}

co daje nam to samo. Możemy temu zaradzić, ponieważ wyliczenia Java zaimplementowane jako pełne klasy z niewielką ilością cukru syntaktycznego posypanego ponad górną krawędź. To jest znowu mniej kotłów, ale nie jest to oczywiste, chyba że idiom jest ci znany. Nie podoba mi się również fakt, że otrzymujesz różne funkcje wyliczania, nawet jeśli nie mają one większego sensu dla singletonu: ordi valuesitp. (W rzeczywistości jest trudniejsza symulacja, w której Color extends Integerbędzie działać z przełącznikiem, ale jest tak trudna, że ​​jest jeszcze bardziej wyraźnie pokazuje, dlaczego enumjest lepszy pomysł).

Bezpieczeństwo wątków

Bezpieczeństwo wątków jest potencjalnym problemem tylko wtedy, gdy singletony są tworzone leniwie, bez blokowania.

public class SingletonClass {
    private static SingletonClass INSTANCE;
    private SingletonClass() {}
    public SingletonClass getInstance() {
        if (INSTANCE == null) INSTANCE = new SingletonClass();
        return INSTANCE;
    }

    // all the methods and instance data for the class here
}

Jeśli wiele wątków wywołuje getInstancejednocześnie, gdy INSTANCEwciąż jest pusta, można utworzyć dowolną liczbę wystąpień. To jest złe. Jedynym rozwiązaniem jest dodanie synchronizeddostępu w celu ochrony zmiennej INSTANCE.

Jednak static finalpowyższy kod nie ma tego problemu. Tworzy instancję chętnie podczas ładowania klasy. Ładowanie klasy jest zsynchronizowane.

enumSingleton jest efektywnie leniwy, ponieważ nie jest inicjowany dopiero pierwszego użycia. Inicjalizacja Java jest również synchronizowana, więc wiele wątków nie może zainicjować więcej niż jednego wystąpienia INSTANCE. Otrzymujesz leniwie zainicjowany singleton z bardzo małą ilością kodu. Jedynym minusem jest raczej niejasna składnia. Musisz znać ten idiom lub dokładnie zrozumieć, jak działają ładowanie i inicjowanie klas, aby wiedzieć, co się dzieje.


15
Ten ostatni akapit naprawdę wyjaśnił mi sytuację singletonu. Dzięki! Każdy czytelnik, który przegląda, powinien to przeczytać ponownie.
Basil Bourque,

Czytałem to, stackoverflow.com/questions/16771373/... . Teraz jestem mylony z twoim ostatnim para. W jaki sposób wyliczanie nie zapewnia funkcji inicjalizacji lay?
Deepankar Singh

static finalpola są inicjowane w czasie inicjalizacji klasy , a nie w czasie ładowania. Jest dokładnie taki sam jak przy enumciągłej inicjalizacji (w rzeczywistości są identyczne pod maską). Dlatego próba wdrożenia „sprytnego” leniwego kodu inicjującego dla singletonów zawsze była bezcelowa, nawet w pierwszej wersji Java.
Holger,

42

Oprócz wspomnianych już przypadków użycia, często uważam, że wyliczenia są przydatne do implementacji wzorca strategii, zgodnie z kilkoma podstawowymi wytycznymi OOP:

  1. Posiadanie kodu, w którym znajdują się dane (to znaczy w obrębie samego wyliczenia - lub często w obrębie stałych wyliczeniowych, które mogą zastąpić metody).
  2. Implementowanie interfejsu (lub więcej), aby nie wiązać kodu klienta z wyliczeniem (który powinien zapewniać tylko zestaw domyślnych implementacji).

Najprostszym przykładem może być zestaw Comparatorimplementacji:

enum StringComparator implements Comparator<String> {
    NATURAL {
        @Override
        public int compare(String s1, String s2) {
            return s1.compareTo(s2);
        }
    },
    REVERSE {
        @Override
        public int compare(String s1, String s2) {
            return NATURAL.compare(s2, s1);
        }
    },
    LENGTH {
        @Override
        public int compare(String s1, String s2) {
            return new Integer(s1.length()).compareTo(s2.length());
        }
    };
}

Ten „wzorzec” może być wykorzystywany w znacznie bardziej złożonych scenariuszach, przy szerokim wykorzystaniu wszystkich korzyści, które pochodzą z wyliczenia: iteracja po instancjach, poleganie na ich domyślnej kolejności, wyszukiwanie instancji po nazwie, metody statyczne zapewniające właściwą instancję dla określonych kontekstów itp. A jednak nadal masz to wszystko ukryte za interfejsem, więc kod będzie działał z niestandardowymi implementacjami bez modyfikacji, na wypadek gdybyś chciał czegoś, co nie jest dostępne w „opcjach domyślnych”.

Widziałem to z powodzeniem stosowane do modelowania pojęcia ziarnistości czasu (codziennie, co tydzień itp.), Gdzie cała logika została zamknięta w wyliczeniu (wybór właściwej ziarnistości dla danego zakresu czasu, specyficzne zachowanie związane z każdą ziarnistością jako stałą metody itp.). A jednak Granularitywarstwa usługi była po prostu interfejsem.


2
Możesz także dodać CASE_INSENSITIVE { @Override public int compare(String s1, String s2) { return s1.compareToIgnoreCase(s2); }. Jedną z niewymienionych jeszcze zalet jest to, że masz solidną obsługę serializacji; trwała forma zawiera tylko nazwę klasy i stałą nazwę, nie zależąc od żadnych szczegółów implementacyjnych twoich komparatorów.
Holger,

32

Coś, czego żadna z pozostałych odpowiedzi nie ujęła, co sprawia, że ​​wyliczenia są szczególnie potężne, to umiejętność stosowania metod szablonowych . Metody mogą być częścią podstawowego wyliczenia i zastępowane przez każdy typ. A przy zachowaniu dołączonym do wyliczenia, często eliminuje to potrzebę konstruowania lub zamiany instrukcji if-else, jak pokazuje ten post na blogu - gdzie enum.method()robi się to, co pierwotnie byłoby wykonywane w warunku. Ten sam przykład pokazuje również użycie importu statycznego z wyliczeniami, a także generowanie znacznie czystszego kodu podobnego do DSL.

Niektóre inne interesujące cechy, to fakt, że teksty stałe zapewnić implementację equals(), toString()a hashCode()i wdrożenia Serializablei Comparable.

Aby uzyskać pełny przegląd wszystkich enum, które mają do zaoferowania, gorąco polecam Bruce Eckel's Thinking in Java 4. wydanie, które poświęca cały rozdział temu tematowi. Szczególnie pouczające są przykłady, w których gra się w kamień, papier, nożyce (tj. RoShamBo) jako wyliczenia.


23

Z dokumentów Java -

Powinieneś używać typów wyliczeniowych za każdym razem, gdy potrzebujesz reprezentować stały zestaw stałych. Obejmuje to naturalne typy wyliczeń, takie jak planety w naszym Układzie Słonecznym i zestawy danych, w których znasz wszystkie możliwe wartości w czasie kompilacji - na przykład opcje w menu, flagi wiersza poleceń i tak dalej.

Typowym przykładem jest zamiana klasy na zestaw prywatnych statycznych stałych końcowych int (w rozsądnej liczbie stałych) na typ wyliczeniowy. Zasadniczo, jeśli uważasz, że znasz wszystkie możliwe wartości „czegoś” w czasie kompilacji, możesz przedstawić to jako typ wyliczenia. Wyliczenia zapewniają czytelność i elastyczność w stosunku do klasy ze stałymi.

Kilka innych zalet, które mogę wymyślić typów wyliczeniowych. Są one zawsze jednym przykładem konkretnej klasy wyliczeniowej (stąd koncepcja użycia wyliczeń w miarę pojawiania się singletonu). Kolejną zaletą jest to, że można użyć enumów jako typu w instrukcji case-switch. Możesz także użyć funkcji toString () na wyliczeniu, aby wydrukować je jako czytelne ciągi znaków.


8
Czy użyłbyś wyliczenia przez wszystkie 366 dni w roku? Jest to stały zestaw (366) stałych (dni się nie zmieniają). To sugeruje, że powinieneś. : - / Ograniczę rozmiar ustalonego zestawu.
moinudin

2
@marcog Przy odrobinie sprytnego myślenia możliwe jest podsumowanie dni tygodnia i dni każdego miesiąca w zwartym wyliczeniu.
James P.

1
Mówiąc o dniach tygodnia… DayOfWeekenum jest teraz wstępnie zdefiniowane, wbudowane w Javę 8 i później w ramach java.time (i przeniesione z powrotem do Java 6 i 7 i Androida ).
Basil Bourque,

19

Dlaczego i po co mam używać enum w codziennym programowaniu?

Możesz użyć Enum do przedstawienia niewielkiego, stałego zestawu stałych lub wewnętrznego trybu klasy, jednocześnie zwiększając czytelność. Ponadto, Enums mogą wymuszać pewną sztywność, gdy są używane w parametrach metody. Oferują one interesującą możliwość przekazywania informacji konstruktorowi, tak jak w przykładzie Planety na stronie Oracle, a także, jak odkryłeś, umożliwiają także prosty sposób na utworzenie wzoru singletonu.

Np .: Locale.setDefault(Locale.US)czyta lepiej niż Locale.setDefault(1)i wymusza użycie ustalonego zestawu wartości pokazanych w IDE, gdy dodajesz .separator zamiast wszystkich liczb całkowitych.


@arnt Naprawiłem również gramatykę w pytaniu. Dzięki.
James P.

13

Enums wyliczyć ustalony zestaw wartości w sposób samok dokumentujący.
Sprawiają, że kod jest bardziej wyraźny, a także mniej podatny na błędy.

Dlaczego nie użyć Stringlub intzamiast Enumstałych?

  1. Kompilator nie zezwala na literówki , ani wartości z ustalonego zestawu, ponieważ wyliczenia same są typami . Konsekwencje:
    • Nie musisz pisać warunku wstępnego (ani instrukcji if), aby upewnić się, że argument znajduje się w prawidłowym zakresie.
    • Typ niezmienna przychodzi za darmo.
  2. Wyliczenia mogą zachowywać się, tak jak każda inna klasa.
  3. W każdym razie prawdopodobnie potrzebujesz podobnej ilości pamięci, aby użyć Strings (zależy to od złożoności Enum).

Co więcej, każda z Enuminstancji jest klasą, dla której można zdefiniować jej indywidualne zachowanie.

Ponadto zapewniają bezpieczeństwo wątków podczas tworzenia instancji (gdy ładunek wyliczany jest), co znalazło świetne zastosowanie w upraszczaniu Wzorca Singletona .

Ten blog ilustruje niektóre z jego aplikacji, takie jak automat stanów dla analizatora składni.


13

Warto wiedzieć, że enumspodobnie jak inne klasy z Constantpolami i a private constructor.

Na przykład,

public enum Weekday
{
  MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
} 

Kompilator kompiluje go w następujący sposób;

class Weekday extends Enum
{
  public static final Weekday MONDAY  = new Weekday( "MONDAY",   0 );
  public static final Weekday TUESDAY = new Weekday( "TUESDAY ", 1 );
  public static final Weekday WEDNESDAY= new Weekday( "WEDNESDAY", 2 );
  public static final Weekday THURSDAY= new Weekday( "THURSDAY", 3 );
  public static final Weekday FRIDAY= new Weekday( "FRIDAY", 4 );
  public static final Weekday SATURDAY= new Weekday( "SATURDAY", 5 );
  public static final Weekday SUNDAY= new Weekday( "SUNDAY", 6 );

  private Weekday( String s, int i )
  {
    super( s, i );
  }

  // other methods...
}

12

enumoznacza enumerację, tj. wspomnienie (kilka rzeczy) jeden po drugim.

Na Enum jest to typ danych, który zawiera fixed zestaw stałych.

LUB

An enumjest jakclass , z ustalonym zestawem instancji znanym w czasie kompilacji.

Na przykład:

public class EnumExample {
    interface SeasonInt {
        String seasonDuration();
    }

    private enum Season implements SeasonInt {
        // except the enum constants remaining code looks same as class
        // enum constants are implicitly public static final we have used all caps to specify them like Constants in Java
        WINTER(88, "DEC - FEB"), SPRING(92, "MAR - JUN"), SUMMER(91, "JUN - AUG"), FALL(90, "SEP - NOV");

        private int days;
        private String months;

        Season(int days, String months) { // note: constructor is by default private 
            this.days = days;
            this.months = months;
        }

        @Override
        public String seasonDuration() {
            return this+" -> "+this.days + "days,   " + this.months+" months";
        }

    }
    public static void main(String[] args) {
        System.out.println(Season.SPRING.seasonDuration());
        for (Season season : Season.values()){
            System.out.println(season.seasonDuration());
        }

    }
}

Zalety enum:

  • enum poprawia bezpieczeństwo typu podczas sprawdzania w czasie kompilacji, aby uniknąć błędów w czasie wykonywania.
  • enum można łatwo zastosować w przełączniku
  • enum można przechodzić
  • enum może mieć pola, konstruktory i metody
  • enum może implementować wiele interfejsów, ale nie może rozszerzyć żadnej klasy, ponieważ wewnętrznie rozszerza klasę Enum

po więcej


9

Co to jest wyliczenie

  • enum jest słowem kluczowym zdefiniowanym dla Enumeration nowego typu danych. Wyliczenia Typesafe powinny być stosowane swobodnie. W szczególności są solidną alternatywą dla prostych stałych String lub int używanych w znacznie starszych interfejsach API do reprezentowania zestawów powiązanych elementów.

Dlaczego warto korzystać z enum

  • wyliczenia są domyślnie końcowymi podklasami java.lang.Enum
  • jeśli wyliczenie jest członkiem klasy, jest domyślnie statyczne
  • nowy nigdy nie może być używany z wyliczeniem, nawet w samym typie wyliczenia
  • nazwa i wartość Po prostu użyj tekstu stałych wyliczeniowych, podczas gdy toString może zostać zastąpione, aby zapewnić dowolną treść, jeśli jest to pożądane
  • dla stałych wyliczeniowych, równa się i == równa się tej samej rzeczy, i mogą być używane zamiennie
  • stałe enum są domyślnie publicznie statyczne, końcowe

Uwaga

  • wyliczenia nie mogą rozszerzać żadnej klasy.
  • Wyliczenie nie może być nadklasą.
  • kolejność pojawiania się stałych wyliczeniowych nazywana jest ich „porządkiem naturalnym” i określa także porządek używany przez inne elementy: CompareTo, kolejność iteracji wartości, EnumSet, EnumSet.range.
  • Wyliczenie może mieć konstruktory, bloki statyczne i instancji, zmienne i metody, ale nie może mieć metod abstrakcyjnych.

4
Nie wyjaśniasz, dlaczego stosować wyliczenia. Po prostu podajesz kilka właściwości wyliczeń w niepowiązanych nagłówkach.
Duncan Jones

@DuncanJones czy terminy nie są ostateczne, statyczne podają cel używania wyliczeń?
tinker_fairy

8

Poza tym, co powiedzieli inni. W starszym projekcie, nad którym kiedyś pracowałem, w dużej komunikacji między jednostkami (niezależnymi aplikacjami) korzystano z liczb całkowitych, które reprezentowały mały zestaw. Przydatne było zadeklarowanie zestawu enummetodami statycznymi, aby uzyskać enumobiekt valuei viceversa. Kod wyglądał na bardziej przejrzysty, zmieniał użyteczność wielkości liter i łatwiejszy zapis do logów.

enum ProtocolType {
    TCP_IP (1, "Transmission Control Protocol"), 
    IP (2, "Internet Protocol"), 
    UDP (3, "User Datagram Protocol");

    public int code;
    public String name;

    private ProtocolType(int code, String name) {
        this.code = code;
        this.name = name;
    }

    public static ProtocolType fromInt(int code) {
    switch(code) {
    case 1:
        return TCP_IP;
    case 2:
        return IP;
    case 3:
        return UDP;
    }

    // we had some exception handling for this
    // as the contract for these was between 2 independent applications
    // liable to change between versions (mostly adding new stuff)
    // but keeping it simple here.
    return null;
    }
}

Utwórz enumobiekt z otrzymanych wartości (np. 1,2) za pomocą opcji ProtocolType.fromInt(2) Zapisz w dziennikach za pomocąmyEnumObj.name

Mam nadzieję że to pomoże.


6

Enum dziedziczy wszystkie metody Objectklasy i klasy abstrakcyjnej Enum. Możesz więc użyć jego metod do refleksji, wielowątkowości, serilizacji, porównywalności itp. Jeśli tylko zadeklarujesz stałą statyczną zamiast Enum, nie możesz. Poza tym wartość Enum można również przekazać do warstwy DAO.

Oto przykładowy program do zademonstrowania.

public enum State {

    Start("1"),
    Wait("1"),
    Notify("2"),
    NotifyAll("3"),
    Run("4"),
    SystemInatilize("5"),
    VendorInatilize("6"),
    test,
    FrameworkInatilize("7");

    public static State getState(String value) {
        return State.Wait;
    }

    private String value;
    State test;

    private State(String value) {
        this.value = value;
    }

    private State() {
    }

    public String getValue() {
        return value;
    }

    public void setCurrentState(State currentState) {
        test = currentState;
    }

    public boolean isNotify() {
        return this.equals(Notify);
    }
}

public class EnumTest {

    State test;

    public void setCurrentState(State currentState) {
        test = currentState;
    }

    public State getCurrentState() {
        return test;
    }

    public static void main(String[] args) {
        System.out.println(State.test);
        System.out.println(State.FrameworkInatilize);
        EnumTest test=new EnumTest();
        test.setCurrentState(State.Notify);
        test. stateSwitch();
    }

    public void stateSwitch() {
        switch (getCurrentState()) {
        case Notify:
            System.out.println("Notify");
            System.out.println(test.isNotify());
            break;
        default:
            break;
        }
    }
}

4

ENum oznacza „Enumerated Type”. Jest to typ danych mający ustalony zestaw stałych, które sam definiujesz.


Nie wyjaśniłeś, dlaczego używać wyliczenia.
Duncan Jones

4
Tak. Używasz go do przechowywania ustalonego zestawu stałych, które sam definiujesz. Konkretne zastosowanie tego zależy od Ciebie. Nie ma czegoś takiego jak powiedzenie, kiedy, dlaczego lub jak czegoś użyć. To zależy od potrzeb dewelopera. Musisz tylko zrozumieć, co to jest i jak działa.
Steve,

4

Moim zdaniem wszystkie odpowiedzi, które do tej pory uzyskałeś, są prawidłowe, ale z mojego doświadczenia, wyraziłbym to w kilku słowach:

Użyj wyliczeń, jeśli chcesz, aby kompilator sprawdzał poprawność wartości identyfikatora.

W przeciwnym razie możesz używać ciągów znaków, jak zawsze (prawdopodobnie zdefiniowałeś pewne „konwencje” dla swojej aplikacji) i będziesz bardzo elastyczny ... ale nie uzyskasz 100% bezpieczeństwa przed literówkami na swoich ciągach i zrozumiesz je tylko w czasie wykonywania.


3

Użyj wyliczeń dla TYPE BEZPIECZEŃSTWA, jest to funkcja językowa, więc zwykle otrzymasz:

  • Obsługa kompilatora (natychmiast zobacz problemy z typem)
  • Obsługa narzędzi w IDE (automatyczne uzupełnianie w przypadku przełącznika, brakujące przypadki, wymuszanie domyślnego, ...)
  • W niektórych przypadkach wydajność wyliczania jest również świetna ( EnumSet , bezpieczna dla typów alternatywa dla tradycyjnych „flag bitowych” ).

Wyliczenia mogą mieć metody, konstruktory, możesz nawet używać wyliczeń wewnątrz wyliczeń i łączyć wyliczenia z interfejsami.

Traktuj wyliczenia jako typy zastępujące dobrze zdefiniowany zestaw stałych int (które Java „odziedziczył” po C / C ++), a w niektórych przypadkach zastępujące flagi bitowe.

Książka Effective Java 2nd Edition zawiera cały rozdział na ich temat i zawiera więcej szczegółów. Zobacz także ten post Przepełnienie stosu .


2

Java pozwala ograniczyć zmienną do posiadania jednej z niewielu predefiniowanych wartości - innymi słowy, jednej wartości z wyliczonej listy. Użycie enumsmoże pomóc zmniejszyć liczbę błędów w kodzie. Oto przykład enumspoza klasą:

enums coffeesize{BIG , HUGE , OVERWHELMING }; 
//This semicolon is optional.

Ogranicza to coffeesizedo konieczności: albo BIG, HUGEalbo OVERWHELMINGjako zmienna.


2

Enum? Dlaczego warto go używać? Myślę, że lepiej to zrozumieć, kiedy będziesz go używać. Mam takie samo doświadczenie.

Powiedz, że masz operację tworzenia, usuwania, edycji i odczytu bazy danych.

Teraz, jeśli utworzysz wyliczenie jako operację:

public enum operation {
    create("1")
    delete("2")
    edit("3")
    read("4")

    // You may have is methods here
    public boolean isCreate() {
        return this.equals(create);
    }
    // More methods like the above can be written

}

Teraz możesz zadeklarować coś takiego:

private operation currentOperation;

// And assign the value for it 
currentOperation = operation.create

Możesz więc używać go na wiele sposobów. Zawsze dobrze jest mieć wyliczenie dla określonych rzeczy, ponieważ operacją bazy danych w powyższym przykładzie można kontrolować, sprawdzając bieżącą Operację . Być może można powiedzieć, że można to osiągnąć również za pomocą zmiennych i liczb całkowitych. Ale wierzę, że Enum to bezpieczniejszy sposób dla programistów.

Inna sprawa: myślę, że każdy programista uwielbia logiczne , prawda? Ponieważ może przechowywać tylko dwie wartości, dwie określone wartości. Można więc uznać, że Enum ma ten sam typ udogodnień, w których użytkownik określi, ile i jaki rodzaj wartości będzie przechowywać, tylko w nieco inny sposób. :)


1

Do tej pory nigdy nie potrzebowałem używać wyliczeń. Czytałem o nich, odkąd zostały wprowadzone w wersji 1.5 lub tygrysa, jak to się nazywało w ciągu dnia. Tak naprawdę nigdy nie rozwiązali dla mnie „problemu”. Dla tych, którzy go używać (i widzę wiele z nich nie), jestem pewien, że zdecydowanie służy jakiś cel. Tylko moje 2 funty.



1

Tym, co dało mi moment Ah-Ha, była ta świadomość: że Enum ma prywatnego konstruktora dostępnego tylko poprzez publiczne wyliczenie:

enum RGB {
    RED("Red"), GREEN("Green"), BLUE("Blue");

    public static final String PREFIX = "color ";

    public String getRGBString() {
        return PREFIX + color;
    }

    String color;

    RGB(String color) {
        this.color = color;
    }
}

public class HelloWorld {
    public static void main(String[] args) {
        String c = RGB.RED.getRGBString();
        System.out.print("Hello " + c);
    }
}

1

Jeśli chodzi o to, aby kod był czytelny w przyszłości, najbardziej przydatny przypadek wyliczenia jest przedstawiony w następnym fragmencie:

public enum Items {
    MESSAGES, CHATS, CITY_ONLINE, FRIENDS, PROFILE, SETTINGS, PEOPLE_SEARCH, CREATE_CHAT
}

@Override
public boolean onCreateOptionsMenu(Menu menuPrm) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.main, menuPrm);
    View itemChooserLcl;
    for (int i = 0; i < menuPrm.size(); i++) {
        MenuItem itemLcl  = menuPrm.getItem(i);
            itemChooserLcl = itemLcl.getActionView();
            if (itemChooserLcl != null) {
                 //here Im marking each View' tag by enume values:
                itemChooserLcl.setTag(Items.values()[i]);
                itemChooserLcl.setOnClickListener(drawerMenuListener);
            }
        }
    return true;
}
private View.OnClickListener drawerMenuListener=new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Items tagLcl= (Items) v.getTag();
        switch (tagLcl){
            case MESSAGES: ;
            break;
            case CHATS : ;
            break;
            case CITY_ONLINE : ;
            break;
            case FRIENDS : ;
            break;
            case  PROFILE: ;
            break;
            case  SETTINGS: ;
            break;
            case  PEOPLE_SEARCH: ;
            break;
            case  CREATE_CHAT: ;
            break;
        }
    }
};

1

Z mojego doświadczenia wynika, że ​​korzystanie z Enum czasami powoduje, że zmiany systemów są bardzo trudne. Jeśli używasz Enum dla zestawu wartości specyficznych dla domeny, które często się zmieniają, i ma wiele innych klas i składników, które od niego zależą, możesz rozważyć nieużywanie Enum.

Na przykład system transakcyjny, który wykorzystuje Enum dla rynków / giełd. Istnieje wiele rynków i jest prawie pewne, że będzie wiele podsystemów, które będą musiały uzyskać dostęp do tej listy rynków. Za każdym razem, gdy chcesz dodać nowy rynek do swojego systemu lub jeśli chcesz usunąć rynek, możliwe jest, że wszystko pod słońcem będzie musiało zostać odbudowane i wydane.

Lepszym przykładem byłoby coś w rodzaju rodzaju kategorii produktu. Załóżmy, że twoje oprogramowanie zarządza zapasami w domu towarowym. Istnieje wiele kategorii produktów i wiele powodów, dla których ta lista kategorii może ulec zmianie. Menedżerowie mogą chcieć zaopatrzyć nową linię produktów, pozbyć się innych linii produktów i ewentualnie od czasu do czasu reorganizować kategorie. Jeśli musisz przebudować i ponownie wdrożyć wszystkie swoje systemy tylko dlatego, że użytkownicy chcą dodać kategorię produktów, to wziąłeś coś, co powinno być proste i szybkie (dodanie kategorii) i sprawiło, że jest to bardzo trudne i wolne.

Podsumowując, wyliczenia są dobre, jeśli reprezentowane dane są bardzo statyczne w czasie i mają ograniczoną liczbę zależności. Ale jeśli dane bardzo się zmieniają i ma wiele zależności, to potrzebujesz czegoś dynamicznego, co nie jest sprawdzane w czasie kompilacji (np. Tabela bazy danych).


1

Singleton oparty na wyliczeniu

nowoczesne spojrzenie na stary problem

Podejście to implementuje singletona, wykorzystując gwarancję Javy, że każda wartość wyliczana jest tworzona w programie Java tylko raz, a wyliczanie zapewnia domyślną obsługę bezpieczeństwa wątków. Ponieważ wartości wyliczenia Java są globalnie dostępne, dlatego można ich używać jako singletonów.

public enum Singleton {
    SINGLETON; 
    public void method() { }
}

Jak to działa? Cóż, drugą linię kodu można uznać za coś takiego:

public final static Singleton SINGLETON = new Singleton(); 

I otrzymujemy stary dobry, wczesny singiel zainicjowany.

Pamiętaj, że ponieważ jest to wyliczenie, zawsze możesz uzyskać dostęp do instancji również poprzez Singleton.INSTANCE:

Singleton s = Singleton.INSTANCE;
Zalety
  • Aby zapobiec tworzeniu kolejnych wystąpień singletonu podczas deserializacji, użyj singletonu opartego na wyliczeniu, ponieważ JVM zajmuje się serializacją wyliczenia. Serializacja i deserializacja enum działają inaczej niż w przypadku zwykłych obiektów Java. Jedyną rzeczą, która jest serializowana, jest nazwa wartości wyliczeniowej. Podczas procesu deserializacji wyliczenievalueOf używana jest metoda z deserializowaną nazwą w celu uzyskania żądanej instancji.
  • Singleton oparty na enum pozwala uchronić się przed atakiem odbicia. Typ wyliczeniowy faktycznie rozszerza Enumklasę Java . Powodem, dla którego odbicie nie może być użyte do utworzenia obiektów typu wyliczeniowego, jest to, że specyfikacja java nie zezwala, a reguła ta jest zakodowana w implementacji newInstancemetody Constructorklasy, która jest zwykle używana do tworzenia obiektów poprzez odbicie:
if ((clazz.getModifiers() & Modifier.ENUM) != 0)
    throw new IllegalArgumentException("Cannot reflectively create enum objects");
  • Enum nie powinno być klonowane, ponieważ musi istnieć dokładnie jedna instancja każdej wartości.
  • Najbardziej lakoniczny kod spośród wszystkich realizacji singletonów.
Niedogodności
  • Singleton oparty na wyliczaniu nie pozwala na leniwą inicjalizację.
  • Jeśli zmieniłeś projekt i chciałeś przekonwertować singleton na multiton, enum nie pozwoli na to. Wzorzec wielotonowy służy do kontrolowanego tworzenia wielu instancji, którym zarządza za pomocą a map. Zamiast pojedynczej instancji na aplikację (np. java.lang.Runtime) Wzorzec wielotonowy zapewnia zamiast tego pojedynczą instancję na klucz .
  • Enum pojawia się tylko w Javie 5, więc nie można go używać we wcześniejszej wersji.

Istnieje kilka realizacji wzoru singletonu, z których każdy ma zalety i wady.

  • Chętnie ładuje singletona
  • Podwójnie sprawdzony singleton blokujący
  • Idiom posiadacza inicjalizacji na żądanie
  • Singleton oparty na wyliczeniu

Szczegółowy opis każdego z nich jest zbyt szczegółowy, więc po prostu umieszczam link do dobrego artykułu - Wszystko, co chcesz wiedzieć o Singleton


0

Używałbym wyliczeń jako przydatnego instrumentu mapującego, unikając wielokrotności, if-else pod warunkiem, że niektóre metody zostaną wdrożone.

public enum Mapping {

    ONE("1"),
    TWO("2");

    private String label;

    private Mapping(String label){
        this.label = label;
    }

    public static Mapping by(String label) {

        for(Mapping m: values() {
            if(m.label.equals(label)) return m;
        }

        return null;
    }

}

Więc metoda by(String label) pozwala uzyskać wartość wyliczoną przez niepoliczone. Ponadto można wymyślić mapowanie między 2 wyliczeniami. Można również spróbować „1 do wielu” lub „wielu do wielu” oprócz domyślnej relacji „jeden do jednego”

Na koniec enumjest klasa Java. Możesz mieć w sobie mainmetodę, która może być przydatna, gdy potrzebujesz natychmiast wykonać kilka operacji mapowania args.


0

Zamiast robić kilka stałych deklaracji

Możesz pogrupować je wszystkie w 1 enum

Wszystko to jest zorganizowane przez wspólną grupę, do której należą


0

Wyliczenia są jak klasy. Podobnie jak klasa, ma także metody i atrybuty.

Różnice między klasami są następujące: 1. stałe enum są publiczne, statyczne, końcowe. 2. Wyliczenia nie można użyć do stworzenia obiektu i nie można rozszerzyć innych klas. Ale może implementować interfejsy.


0

Oprócz @BradB Odpowiedź:

To prawda ... Dziwne, że to jedyna odpowiedź, która o tym wspomina. Kiedy początkujący odkryją wyliczenia, szybko traktują to jako magiczną sztuczkę do sprawdzania poprawności identyfikatora kompilatora. A kiedy kod ma być używany w systemach rozproszonych, płaczą ... jakiś miesiąc później. Utrzymanie wstecznej zgodności z wyliczeniami, które zawierają niestatyczną listę wartości, jest prawdziwym problemem i powoduje ból. Wynika to z faktu, że po dodaniu wartości do istniejącego wyliczenia zmienia się jego typ (pomimo nazwy nie).

„Ho, czekaj, może to wyglądać na ten sam typ, prawda? W końcu to wyliczenia o tej samej nazwie - i czy wyliczenia nie są tylko liczbami całkowitymi pod maską?” Z tych powodów twój kompilator prawdopodobnie nie oznaczy użycia jednej definicji samego typu tam, gdzie oczekiwał drugiej. Ale w rzeczywistości są to (w najważniejszych aspektach) różne typy. Co najważniejsze, mają różne domeny danych - wartości, które są dopuszczalne z uwagi na typ. Dodając wartość, skutecznie zmieniliśmy typ wyliczenia, a tym samym zerwać wsteczną kompatybilność.

Podsumowując: używaj go, kiedy chcesz, ale sprawdź, czy używana domena danych jest skończonym, już znanym, stałym zestawem .

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.