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 enum
s i programuję w Javie od ponad kilku lat. I najwyraźniej bardzo się zmienili. Teraz nawet wykonują pełne wsparcie OOP w sobie.
Odpowiedzi:
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?
przeciw
Wywołanie metody takie jak:
następnie staje się:
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
nie jest już możliwe.
źródło
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 logicznymor
przetwarzaniem) może być przydatne w aplikacjach czasu rzeczywistego, w których brakuje pamięci.Dlaczego warto korzystać z funkcji języka programowania? Powód, dla którego w ogóle mamy języki
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:
Teraz możesz napisać:
Powyższa płyta ma podobny efekt jak
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 final
powyż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ć:switch
enum Color
Pamiętaj, że nie ma tego
Color.RED
w przypadkach. Jeśli nie używasz wyliczenia, jedynym sposobem użycia nazwanych ilościswitch
jest coś takiego:Ale teraz zmienna do przechowywania koloru musi mieć typ
int
. Ładne sprawdzanie wyliczenia istatic final
symulacji przez kompilator zniknęło. Nieszczęśliwy.Kompromisem jest użycie elementu symulacji o wartości skalarnej:
Teraz:
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ć:
a następnie dostęp do niego za pomocą
możemy po prostu powiedzieć
co daje nam to samo. Możemy temu zaradzić, ponieważ wyliczenia Java są 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:
ord
ivalues
itp. (W rzeczywistości jest trudniejsza symulacja, w którejColor extends Integer
będzie działać z przełącznikiem, ale jest tak trudna, że jest jeszcze bardziej wyraźnie pokazuje, dlaczegoenum
jest lepszy pomysł).Bezpieczeństwo wątków
Bezpieczeństwo wątków jest potencjalnym problemem tylko wtedy, gdy singletony są tworzone leniwie, bez blokowania.
Jeśli wiele wątków wywołuje
getInstance
jednocześnie, gdyINSTANCE
wciąż jest pusta, można utworzyć dowolną liczbę wystąpień. To jest złe. Jedynym rozwiązaniem jest dodaniesynchronized
dostępu w celu ochrony zmiennejINSTANCE
.Jednak
static final
powyższy kod nie ma tego problemu. Tworzy instancję chętnie podczas ładowania klasy. Ładowanie klasy jest zsynchronizowane.enum
Singleton 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ąpieniaINSTANCE
. 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.źródło
static final
pola są inicjowane w czasie inicjalizacji klasy , a nie w czasie ładowania. Jest dokładnie taki sam jak przyenum
cią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.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:
Najprostszym przykładem może być zestaw
Comparator
implementacji: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
Granularity
warstwa usługi była po prostu interfejsem.źródło
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.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()
ahashCode()
i wdrożeniaSerializable
iComparable
.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.
źródło
Z dokumentów Java -
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.
źródło
DayOfWeek
enum 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 ).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.źródło
Enum
s 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ć
String
lubint
zamiastEnum
stałych?if
), aby upewnić się, że argument znajduje się w prawidłowym zakresie.String
s (zależy to od złożonościEnum
).Co więcej, każda z
Enum
instancji 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.
źródło
Warto wiedzieć, że
enums
podobnie jak inne klasy zConstant
polami i aprivate constructor
.Na przykład,
Kompilator kompiluje go w następujący sposób;
źródło
enum
oznacza enumerację, tj. wspomnienie (kilka rzeczy) jeden po drugim.LUB
Na przykład:
Zalety enum:
po więcej
źródło
Co to jest wyliczenie
Dlaczego warto korzystać z enum
Uwaga
źródło
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
enum
metodami statycznymi, aby uzyskaćenum
obiektvalue
i viceversa. Kod wyglądał na bardziej przejrzysty, zmieniał użyteczność wielkości liter i łatwiejszy zapis do logów.Utwórz
enum
obiekt z otrzymanych wartości (np. 1,2) za pomocą opcjiProtocolType.fromInt(2)
Zapisz w dziennikach za pomocąmyEnumObj.name
Mam nadzieję że to pomoże.
źródło
Enum dziedziczy wszystkie metody
Object
klasy i klasy abstrakcyjnejEnum
. 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.
źródło
ENum oznacza „Enumerated Type”. Jest to typ danych mający ustalony zestaw stałych, które sam definiujesz.
źródło
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.
źródło
Użyj wyliczeń dla TYPE BEZPIECZEŃSTWA, jest to funkcja językowa, więc zwykle otrzymasz:
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 .
źródło
Java pozwala ograniczyć zmienną do posiadania jednej z niewielu predefiniowanych wartości - innymi słowy, jednej wartości z wyliczonej listy. Użycie
enums
może pomóc zmniejszyć liczbę błędów w kodzie. Oto przykładenums
poza klasą:Ogranicza to
coffeesize
do konieczności: alboBIG
,HUGE
alboOVERWHELMING
jako zmienna.źródło
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ę:
Teraz możesz zadeklarować coś takiego:
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. :)
źródło
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.
źródło
Istnieje wiele odpowiedzi tutaj, wystarczy wskazać dwie konkretne:
1) Używanie jako stałych w
Switch-case
instrukcji. Przełącz wielkość liter nie zezwala na używanie obiektów String dla wielkości liter. Przydaje się enums. Więcej: http://www.javabeat.net/2009/02/how-to-use-enum-in-switch/2) Wdrażanie
Singleton Design Pattern
- Enum znów przychodzi na ratunek. Zastosowanie tutaj: Jakie jest najlepsze podejście do używania Enum jako singletonu w Javie?źródło
Tym, co dało mi moment Ah-Ha, była ta świadomość: że Enum ma prywatnego konstruktora dostępnego tylko poprzez publiczne wyliczenie:
źródło
Jeśli chodzi o to, aby kod był czytelny w przyszłości, najbardziej przydatny przypadek wyliczenia jest przedstawiony w następnym fragmencie:
źródło
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).
źródło
Singleton oparty na wyliczeniu
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.
Jak to działa? Cóż, drugą linię kodu można uznać za coś takiego:
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
ZaletySingleton.INSTANCE
:valueOf
używana jest metoda z deserializowaną nazwą w celu uzyskania żądanej instancji.Enum
klasę 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 implementacjinewInstance
metodyConstructor
klasy, która jest zwykle używana do tworzenia obiektów poprzez odbicie:map
. Zamiast pojedynczej instancji na aplikację (np.java.lang.Runtime
) Wzorzec wielotonowy zapewnia zamiast tego pojedynczą instancję na klucz .Istnieje kilka realizacji wzoru singletonu, z których każdy ma zalety i wady.
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
źródło
Używałbym wyliczeń jako przydatnego instrumentu mapującego, unikając wielokrotności,
if-else
pod warunkiem, że niektóre metody zostaną wdrożone.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
enum
jest klasa Java. Możesz mieć w sobiemain
metodę, która może być przydatna, gdy potrzebujesz natychmiast wykonać kilka operacji mapowaniaargs
.źródło
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żą
źródło
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.
źródło
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 .
źródło