Bardzo dobrze znam C #, ale zaczynam więcej pracować w Javie. Spodziewałem się, że wyliczenia w Javie są w zasadzie równoważne z tymi w C #, ale najwyraźniej tak nie jest. Początkowo byłem podekscytowany, gdy dowiedziałem się, że wyliczenia Java mogą zawierać wiele fragmentów danych, co wydaje się bardzo korzystne ( http://docs.oracle.com/javase/tutorial/java/javaOO/enum.html ). Jednak od tego czasu odkryłem, że brakuje wielu funkcji, które są trywialne w C #, takich jak możliwość łatwego przypisania elementowi wyliczenia określonej wartości, aw konsekwencji możliwość konwersji liczby całkowitej na wyliczenie bez przyzwoitego wysiłku ( tj. Konwertuj wartość całkowitą na pasującą wartość Java Enum ).
Więc moje pytanie brzmi: czy wyliczenia Java mają jakąś korzyść w porównaniu z klasą z kilkoma publicznymi statycznymi polami końcowymi? A może po prostu zapewnia bardziej zwartą składnię?
EDYCJA: Pozwól, że wyrażę się bardziej jasno. Jaka jest przewaga wyliczeń Java w porównaniu z klasą z kilkoma publicznymi statycznymi polami końcowymi tego samego typu ? Na przykład w przykładzie planet w pierwszym łączu, jaka jest przewaga wyliczenia nad klasą z tymi stałymi publicznymi:
public static final Planet MERCURY = new Planet(3.303e+23, 2.4397e6);
public static final Planet VENUS = new Planet(4.869e+24, 6.0518e6);
public static final Planet EARTH = new Planet(5.976e+24, 6.37814e6);
public static final Planet MARS = new Planet(6.421e+23, 3.3972e6);
public static final Planet JUPITER = new Planet(1.9e+27, 7.1492e7);
public static final Planet SATURN = new Planet(5.688e+26, 6.0268e7);
public static final Planet URANUS = new Planet(8.686e+25, 2.5559e7);
public static final Planet NEPTUNE = new Planet(1.024e+26, 2.4746e7);
O ile wiem, odpowiedź Casablanki jest jedyną, która to spełnia.
public static final
polach, które mogą być wpisanymi wartościami, a niekoniecznieint
s.S
, więc teraz mówimy o prawdopodobnie przekazywaniu ich do funkcji itp. Czy potrzebujemy bezpieczeństwa typów? Cóż, nie, ale większość ludzi uważa, że „wszystko jest wvoid*
dobrym stylu” w stylu c i może to powstrzymać błędy (zwłaszcza jeśli przekazuje więcej niż jeden parametr wyliczenia / ciągu znaków!). Umieszcza również stałe w ich własnej przestrzeni nazw, itp. W przeciwieństwie do tego, posiadanie zwykłych zmiennych nie ma prawdziwej korzyści.int
s nie ma bezpieczeństwa typu, ponieważ można przekazać dowolną wartość. Z drugiej strony obiekty typowane nie różnią się od wyliczeń pod względem bezpieczeństwa typów.Odpowiedzi:
Technicznie rzecz biorąc, można by rzeczywiście postrzegać wyliczenia jako klasę z kilkoma typami stałymi, i tak faktycznie jest sposób, w jaki stałe wyliczeniowe są implementowane wewnętrznie. Użycie a
enum
daje jednak przydatne metody ( Enum javadoc ), które w innym przypadku musiałbyś zaimplementować samodzielnie, takie jakEnum.valueOf
.źródło
.values()
do iteracji po liście wartości.switch
rachunkucase
sprawozdaniu bez zastrzeżeń.ordinal().
EnumSet
iEnumMap
zajęcia.źródło
switch
stwierdzeń, które były pierwotną motywacją do użyciaEnum
w ogóle.Nikt nie wspomniał o możliwości ich wykorzystania w
switch
wypowiedziach; To też dorzucę.Pozwala to na używanie dowolnie złożonych wyliczeń w czysty sposób bez używania
instanceof
, potencjalnie mylącychif
sekwencji lub wartości przełączających innych niż łańcuch / int. Przykładem kanonicznym jest automat stanowy.źródło
Podstawową zaletą jest bezpieczeństwo typów. W przypadku zestawu stałych można użyć dowolnej wartości tego samego typu wewnętrznego, wprowadzając błędy. W przypadku wyliczenia można używać tylko odpowiednich wartości.
Na przykład
vs
źródło
setSize(null)
w drugim przykładzie, ale prawdopodobnie zakończy się niepowodzeniem znacznie wcześniej niż błąd pierwszego przykładu.Jest mniej zamieszania. Weźmy
Font
na przykład. Ma konstruktora, który przyjmuje żądaną nazwęFont
, jej rozmiar i styl (new Font(String, int, int)
). Do dziś nie pamiętam, czy styl, czy rozmiar są najważniejsze. JeżeliFont
nie stosuje sięenum
do wszystkich swoich różnych stylów (PLAIN
,BOLD
,ITALIC
,BOLD_ITALIC
), jego konstruktor będzie wyglądaćFont(String, Style, int)
, uniemożliwiając wszelkie nieporozumienia. Niestety,enum
nie było go w pobliżu, kiedyFont
klasa została utworzona, a ponieważ Java musi zachować kompatybilność wsteczną, zawsze będziemy nękani tą niejednoznacznością.Oczywiście jest to tylko argument przemawiający za używaniem stałych
enum
zamiastpublic static final
. Wyliczenia są również idealne dla singletonów i implementują domyślne zachowanie, umożliwiając późniejsze dostosowywanie (tj. Wzorzec strategii ). Przykładem tego ostatniego jestjava.nio.file
„sOpenOption
aStandardOpenOption
: jeśli deweloper chciał stworzyć własne niestandardoweOpenOption
, potrafił.źródło
enum
bez użycia varargs lub a,Set
aby wziąć dowolną ich liczbę.interface Foo { public void bar(String baz); }
. Ktoś sprawia klasy, która wywołujebar
:someFoo.bar(baz = "hello");
. Zmieniam podpisFoo::bar
napublic void bar(String foobar)
. Teraz osoba, która dzwoniła,someFoo
będzie musiała zmodyfikować swój kod, jeśli nadal chce, aby działał.Istnieje wiele dobrych odpowiedzi, ale nikt nie wspomina, że istnieją wysoce zoptymalizowane implementacje klas / interfejsów Collection API specjalnie dla wyliczeń :
Te specyficzne klasy wyliczenia akceptują tylko
Enum
wystąpienia (EnumMap
jedyne akceptowaneEnum
s tylko jako klucze) i jeśli to możliwe, powracają do kompaktowej reprezentacji i manipulacji bitami w ich implementacji.Co to znaczy?
Jeśli nasz
Enum
typ ma nie więcej niż 64 elementy (większość rzeczywistychEnum
przykładów będzie się do tego kwalifikować), implementacje przechowują elementy w jednejlong
wartości, każdaEnum
rozpatrywana instancja będzie powiązana z bitem o długości 64-bitowejlong
. Dodanie elementu do elementuEnumSet
to po prostu ustawienie odpowiedniego bitu na 1, a usunięcie go to po prostu ustawienie tego bitu na 0. Testowanie, czy element jest w elemencie,Set
to tylko jeden test maski bitowej! Teraz musisz to pokochaćEnum
!źródło
What does this mean?
sekcji. Wiedziałem, że jest podział we wdrażaniu w rozmiarze 64, ale tak naprawdę nie wiedziałem dlaczegoprzykład:
Ograniczenie stałych Java
1) Brak bezpieczeństwa typu : Przede wszystkim nie jest bezpieczny; możesz przypisać dowolną prawidłową wartość int do int, np. 99, chociaż nie ma monety, która by reprezentowała tę wartość.
2) Brak sensownego drukowania : wartość drukowania którejkolwiek z tych stałych wypisze jej wartość numeryczną zamiast znaczącej nazwy monety np. Kiedy wydrukujesz NICKLE, wypisze "5" zamiast "NICKLE"
3) Brak przestrzeni nazw : aby uzyskać dostęp do stałej currencyDenom, musimy poprzedzić nazwę klasy, np. CurrencyDenom.PENNY zamiast po prostu używać PENNY, chociaż można to również osiągnąć za pomocą importu statycznego w JDK 1.5
Zaleta enum
1) Wyliczenia w Javie są bezpieczne dla typów i mają własną przestrzeń nazw. Oznacza to, że Twoje wyliczenie będzie miało typ, na przykład „Waluta” w poniższym przykładzie i nie możesz przypisać żadnej wartości innej niż określona w stałych wyliczeniowych.
Currency coin = Currency.PENNY; coin = 1; //compilation error
2) Enum w Javie są typami referencyjnymi, takimi jak klasa lub interfejs i możesz zdefiniować konstruktor, metody i zmienne wewnątrz java Enum, co czyni go bardziej wydajnym niż Enum w C i C ++, jak pokazano w następnym przykładzie typu Java Enum.
3) Możesz określić wartości stałych wyliczeniowych w czasie tworzenia, jak pokazano na poniższym przykładzie: publiczne wyliczenie Waluta {PENNY (1), NICKLE (5), DIME (10), QUARTER (25)}; Ale aby to zadziałało, musisz zdefiniować zmienną składową i konstruktora, ponieważ PENNY (1) w rzeczywistości wywołuje konstruktor, który akceptuje wartość int, patrz poniższy przykład.
Źródła: https://javarevisited.blogspot.com/2011/08/enum-in-java-example-tutorial.html
źródło
Jak już zauważyłeś, pierwszą zaletą wyliczeń jest prostota składni. Ale głównym celem wyliczeń jest zapewnienie dobrze znanego zestawu stałych, które domyślnie tworzą zakres i pomagają w przeprowadzaniu bardziej kompleksowej analizy kodu poprzez sprawdzanie bezpieczeństwa typu i wartości.
Te atrybuty wyliczeń pomagają zarówno programiście, jak i kompilatorowi. Na przykład, powiedzmy, że widzisz funkcję, która akceptuje liczbę całkowitą. Co ta liczba całkowita może oznaczać? Jakie wartości możesz przekazać? Tak naprawdę nie wiesz od razu. Ale jeśli widzisz funkcję, która akceptuje wyliczenie, znasz bardzo dobrze wszystkie możliwe wartości, które możesz przekazać.
W przypadku kompilatora wyliczenia pomagają określić zakres wartości i jeśli nie przypiszesz specjalnych wartości do elementów członkowskich wyliczenia, będą to zakresy od 0 w górę. Pomaga to w automatycznym śledzeniu błędów w kodzie poprzez sprawdzanie bezpieczeństwa typu i nie tylko. Na przykład kompilator może ostrzec, że nie obsługujesz wszystkich możliwych wartości wyliczenia w instrukcji switch (tj. Gdy nie masz
default
wielkości liter i obsługujesz tylko jedną z N wartości wyliczenia). Ostrzega również, gdy konwertujesz dowolną liczbę całkowitą na wyliczenie, ponieważ zakres wartości wyliczenia jest mniejszy niż liczba całkowita, a to z kolei może powodować błędy w funkcji, która tak naprawdę nie akceptuje liczby całkowitej. Ponadto generowanie tabeli skoków dla przełącznika staje się łatwiejsze, gdy wartości są od 0 w górę.Dotyczy to nie tylko języka Java, ale także innych języków ze ścisłym sprawdzaniem typu. C, C ++, D, C # to dobre przykłady.
źródło
Wyliczenie jest niejawnie ostateczne, z prywatnymi konstruktorami, wszystkie jego wartości są tego samego typu lub podtypu, wszystkie jego wartości można uzyskać za pomocą
values()
, pobraćname()
lubordinal()
wartość lub można wyszukać wyliczenie według numeru lub nazwy.Możesz także zdefiniować podklasy (chociaż teoretycznie ostateczne, coś, czego nie możesz zrobić w żaden inny sposób)
źródło
Zalety enum:
„np. możliwość łatwego przypisania elementowi wyliczenia określonej wartości”
„iw konsekwencji możliwość konwersji liczby całkowitej na wyliczenie bez znacznego wysiłku” Dodaj metodę konwertującą int na wyliczenie, która to robi. Po prostu dodaj statyczny HashMap <Integer, EnumX> zawierający mapowanie wyliczenia java .
Jeśli naprawdę chcesz przekonwertować ord = VAL_200.ordinal () z powrotem na val_200, po prostu użyj: EnumX.values () [ord]
źródło
Inną ważną różnicą jest to, że kompilator java traktuje
static final
pola typów pierwotnych i ciągów jako literały. Oznacza to, że te stałe stają się wbudowane. Jest podobny doC/C++
#define
preprocesora. Zobacz to pytanie SO . Tak nie jest w przypadku wyliczeń.źródło
Korzystając z wyliczenia, uzyskujesz sprawdzanie czasu kompilacji prawidłowych wartości. Spójrz na to pytanie.
źródło
Największą zaletą jest to, że enum Singletony są łatwe do napisania i bezpieczne dla wątków:
i
oba są podobne i same obsłużyły serializację, implementując
więcej
źródło
Myślę, że
enum
nie możnafinal
, ponieważ kompilator pod maską generuje podklasy dla każdegoenum
wpisu.Więcej informacji Ze źródła
źródło
Publikowane tutaj wyliczenia mają wiele zalet i właśnie teraz tworzę takie wyliczenia, o które zadano w pytaniu. Ale mam wyliczenie z 5-6 polami.
W tego rodzaju przypadkach, gdy masz wiele pól w wyliczeniach, bardzo trudno jest zrozumieć, która wartość należy do którego pola, ponieważ musisz zobaczyć konstruktor i gałkę oczną.
Klasa ze
static final
stałymi i używanieBuilder
wzorca do tworzenia takich obiektów sprawia, że jest bardziej czytelny. Ale stracisz wszystkie inne zalety używania wyliczenia, jeśli ich potrzebujesz. Wadą takich klas jest, trzeba dodaćPlanet
ręcznie do obiektówlist/set
oPlanets.
I wciąż wolą enum nad taką klasą, jak
values()
jest przydatna i nigdy nie wiadomo, czy trzeba je stosować wswitch
lubEnumSet
czyEnumMap
w przyszłości :)źródło
Główny powód: wyliczenia pomagają pisać dobrze ustrukturyzowany kod, w którym semantyczne znaczenie parametrów jest jasne i silnie wpisane w czasie kompilacji - ze wszystkich powodów, które podały inne odpowiedzi.
Quid pro quo: w Javie po wyjęciu z pudełka tablica elementów Enum jest ostateczna. Zwykle jest to dobre, ponieważ pomaga docenić bezpieczeństwo i testowanie, ale w niektórych sytuacjach może to być wada, na przykład jeśli rozszerzasz istniejący kod bazowy, być może z biblioteki. W przeciwieństwie do tego, jeśli te same dane znajdują się w klasie z polami statycznymi, możesz łatwo dodać nowe instancje tej klasy w czasie wykonywania (może być również konieczne napisanie kodu, aby dodać je do dowolnej iterowalnej dla tej klasy). Ale to zachowanie Enum można zmienić: używając refleksji możesz dodawać nowych członków w czasie wykonywania lub zastępować istniejących członków, chociaż powinno to prawdopodobnie robić tylko w wyspecjalizowanych sytuacjach, w których nie ma alternatywy: tj. Jest to hackowe rozwiązanie i może powodować nieoczekiwane problemy, zobacz moją odpowiedźCzy mogę dodawać i usuwać elementy wyliczenia w czasie wykonywania w Javie .
źródło