2009-04-07 16 views
11

W języku Java Enum może robić wspaniałe rzeczy, które wylicza Enums, ale może również posiadać metody (zachowanie i logikę). Jaka jest przewaga nad używaniem klasy przy użyciu enum? Proste przykłady ilustrujące ten punkt również byłyby mile widziane.Java Enums może mieć zachowanie?

Odpowiedz

14

Oto prosty przykład:

enum RoundingMode { 
    UP { 
     public double round(double d) { 
      return Math.ceil(d); 
     } 
    }, 
    DOWN { 
     public double round(double d) { 
      return Math.floor(d); 
     } 
    }; 

    public abstract double round(double d); 
} 
+0

W jaki sposób użyłbyś tego w kodzie? Czy po prostu to robię? double foo = RoundingMode.round (20.3); A może rozszerzyłbyś wyliczenie na enum dziecka? Jestem odrzucony przez abstrakcyjną metodę. – Dave

+0

Metoda abstrakcyjna jest nadpisywana przez zagnieżdżone klasy używane przez UP i DOWN. Używałbyś go w kodzie jak poniżej: double doMath (RoundMode m, double x, double y) {double d; ... d = m.round (d); ... return d; } Jako pierwszy parametr przekazujesz RoundingMode.UP lub RoundingMode.DOWN. –

+0

Dzięki! Nie jestem pewien jak się z tym czuję, ale rozumiem to teraz;) – Dave

6

Nie jestem do końca pewien, czy tytuł pytania pasuje do reszty. Tak, Java wylicza zachowania. Mogą też mieć stan, chociaż naprawdę powinien on być niezmienny. (Pomysł na zmienną wartość wyliczenia jest dość przerażającym IMO.)

Wyliczenie w Javie jest w zasadzie ustalonym zbiorem obiektów. Zaletą jest to, że wiesz, że jeśli masz referencję tego typu, to zawsze jest to jeden z dobrze znanych zestawów.

Osobiście uwielbiam wyliczanki Java i życzę, aby C# również je posiadało - są bardziej zorientowane obiektowo niż wyliczenia C#, które są w zasadzie "nazwanymi liczbami". Istnieje kilka "gotch" w zakresie kolejności inicjalizacji, ale są one generalnie super.

+0

nie można naśladować stałe teksty w języku Java z C# klas? –

+0

@Vilx: Oczywiście, że możesz. Przed wprowadzeniem języka Java 5 można było pisać własne klasy, aby tworzyć obiekty enum z typami safe. (Cóż, nadal możesz, oczywiście, nie ma po co.) – Eddie

+0

Vilx: Możesz, ale jest trochę nieprzyjemny. Zasadniczo używasz abstrakcyjnego typu bazowego z prywatnym konstruktorem i wyprowadzasz z niego * typy * zagnieżdżone, które mogą wywoływać prywatny konstruktor. –

1

W naszym projekcie używamy Enums dla kilku rzeczy, ale być może najważniejszego dla celów i18n - każdy kawałek pokazanego tekstu otrzymuje Enum. Klasa Enum ma metodę zwracającą ciąg, która sprawdza używane ustawienia regionalne i wybiera poprawne tłumaczenie z kolekcji tłumaczeń w środowisku wykonawczym.

Służy to podwójnemu celowi - otrzymujesz kodowanie z IDE, a także nigdy nie zapomnisz przetłumaczyć napisu.

Użycie jest bardzo proste, do tego stopnia, że ​​prawie rendundant dać przykład, ale oto jak można by użyć Translation-enum

System.out.println(Translations.GREET_PERSON.trans()+" "+user.getName()); 

Albo, jeśli chcesz być wyobraźnia, mają enum przyjąć argumenty, które z jakiegoś magicznego ciąg manipulacji, które są wkładane w zaznaczonej pozycji w łańcuchu tłumaczenia

System.out.println(Translations.GREET_PERSON.trans(user.getName()); 
+0

Interesujące użycie, ale wydaje się, że można uzyskać to samo zachowanie z serii metod statycznych w klasie. Przypuszczam, że nie widzę korzyści z używania enum. – Dave

+0

Wyliczenia są znacznie łatwiejsze do dodania w razie potrzeby. Są również świadomi siebie - możesz pobrać ciąg enum sam w ramach trans() - dlatego możesz użyć tego jako słowa kluczowego do dopasowania w swojej tabeli tłumaczeń –

8

Enum typy to także świetny sposób na wdrożenie prawdziwych singletonów.

Klasyczne wzory singletonowe w języku Java zazwyczaj dotyczą prywatnych konstruktorów i publicznych metod statycznych, ale nadal są podatne na instancję poprzez odbicie lub (serializację). Ochrona typu enum przeciwko temu.

+0

Mimo, że dostajesz jakąś dziwną metodę "za darmo". (W każdym razie singletony są złe.) –

+0

Nie rozumiem komentarza. –

+0

Interesujący punkt. W jaki sposób enum chroni przed odbiciem i deserializacją? Sądzę, że pytam, co właściwie przeszkadza mi w tworzeniu instancji za pomocą refleksji? Czy kompilator rzuca sprawdzony wyjątek? – Dave

2

Ponieważ instancje wyliczeniowe są pojedynczymi literami, można ich używać w instrukcjach switch lub przy użyciu == w celu sprawdzenia równości.

2

Zasadniczo, Java wylicza są klasy (nie wierzę, że istnieje różnica na poziomie bajtode), z dodatkową korzyścią posiadania znanego ustalonego zestawu możliwych wystąpień i możliwość używania ich w instrukcjach przełączania .

Możesz emulować "znany ustalony zestaw możliwych instancji" za pomocą zwykłych klas (wzór "enumeratywnie bezpieczny" opisany w niezliczonych książkach i artykułach), ale jest to dość pracochłonne (powtarzane dla każdej takiej klasy), aby uzyskać go działa naprawdę poprawnie w odniesieniu do Serializacji, equals() i hashCode(), i być może kilka innych rzeczy, o których zapomniałem. Wyróżnienia na poziomie języka oszczędzają ci tę pracę. I, jak wspomniano powyżej, w instrukcjach przełączników można używać wyłącznie wyrażeń na poziomie języka.

+0

Są to klasy, ale istnieje różnica na poziomie bajtodu. Po pierwsze, klasy wyliczeniowe mają flagę ACC_ENUM. Zobacz http://java.sun.com/docs/books/jvms/second_edition/ClassFileFormat-Java5.pdf. –

0

Przyjrzyj się klasom czasowym java/joda, w których można znaleźć mnóstwo pracy.

Oto przykład java.time.Month:

public enum Month implements TemporalAccessor, TemporalAdjuster { 
    JANUARY, 
    FEBRUARY, 
    MARCH, 
    APRIL, 
    MAY, 
    JUNE, 
    JULY, 
    AUGUST, 
    SEPTEMBER, 
    OCTOBER, 
    NOVEMBER, 
    DECEMBER; 

    private static final Month[] ENUMS = Month.values(); 

    public static Month of(int month) { 
     if (month < 1 || month > 12) { 
      throw new DateTimeException("Invalid value for MonthOfYear: " + month); 
     } 
     return ENUMS[month - 1]; 
    } 

    // About a dozen of other useful methods go here 

}