2012-01-03 21 views
5

Próbuję znaleźć odpowiednik Java dla następnego wygody w C++:C-jak enum w Javie

enum { 
    ANIMAL_CAT = 0, 
    ANIMAL_RAT, 
    ANIMAL_BAT, ... 
    NUM_ANIMALS 
}; 

Animal animals[NUM_ANIMALS]; 

animals[ANIMAL_CAT].mNumLegs = 4; 
animals[ANIMAL_RAT].mNumLegs = 4; ... 

wiem, że to nie jest najfajniejsza rzecz na świecie, ale mogę dodać nowy ANIMAL_xxx w dowolnym miejscu w enum i wszystkie poniższe wpisy zostaną automatycznie dostosowane. Czy jest to czysty sposób na to w Javie?


Dzięki za odpowiedzi, ale mógłbym nawiązywać do większej prostoty, niż zamierzałem.

Pracuję nad grą, w której jest fizyka, silnik sztucznej inteligencji itp. Próbuję skonsolidować wszystkie informacje potrzebne dla każdego rodzaju jednostki (niektóre mają tylko fizyczną manifestację, podczas gdy niektóre mają zarówno phys i AI, etc ...), dzięki czemu mogę łatwo dodawać/modyfikować typy (tak, zwierzę jest złym przykładem, ponieważ potrzebuję kamieni, roślin itp.).

Używam klasy do przechowywania informacji o typie, podczas gdy inne klasy polegają na tej klasie, aby wyszukać atrybuty (wymiary, indeks bitmapowy, hasAi itd.). W końcu próbuję oczyścić coś podobnego do następującego, pozwalając jednocześnie mi łatwo dodawać nowe typy:

class UnitTypeInfo { 
    UnitTypeInfo() { 
    mTypes = new TypeInfo[NUM_TYPES]; 

    // and allocate... 

    // initialize TypeInfos here 
    mTypes[TYPE_CAT].mType = TYPE_CAT; 
    mTypes[TYPE_CAT].mMass = 10.0f; 
    mTypes[TYPE_CAT] ... 

    mTypes[TYPE_ROCK].mType = TYPE_ROCK; 
    ... 
    } 

    public class TypeInfo { 
    public int mType; 
    public int mRawResourceHandle; 
    public float mMass; 
    public Vector2d mDimensions; 
    public float mHealth; 
    public boolean mHasAi; 
    ... 
    } 

    public static final int TYPE_CAT = 0; 
    public static final int TYPE_ROCK = 1; 
    public static final int TYPE_TREE = 2; ... 
    public static final int NUM_TYPES = ???; // the last TYPE_ + 1 

    public TypeInfo mTypes[]; 
} 

teraz wydaje się to jak jakiś implementacji XML może być „prawo” rzeczy do zrobienia, ale nie jestem pewien o tym, jak to zrobić (nowość w Javie). W każdym razie, inne klasy mogą z łatwością po prostu użyć unitTypeInfo.mTypes [UnitTypeInfo.TYPE_CAT] .mMass (utworzony), aby sprawdzić masę kota. Jednakże, jeśli chcę dodać TYPE_DOG pod definicją TYPE_CAT, muszę zaktualizować wszystko pod nim (leniwy, wiem, ale załóżmy, że jest jeszcze kilka typów).

Obliczenia są łatwym rozwiązaniem tego problemu w C++, ale w Javie, najprostsze rozwiązanie, jakie mogę wykonać teraz, wymaga czegoś takiego jak: unitTypeInfo.mTypes [UnitTypeInfo.mTypeEnum.TYPE_CAT.ordinal()]. ​​mMass - i chociaż przypuszczam, że to nie jest dużo gorzej niż moje już marne rozwiązanie, dodaje trochę więcej pośredniego i rzeczywistego wywołania metody (do metody, której większość źródeł nie zachęca).

Jeszcze jedno jest to, że chcę mieć możliwość wywołania przełącznika (typu), co może również ograniczyć możliwe rozwiązania.

W każdym razie zaczynam rozumieć, że musi być znacznie lepsze rozwiązanie tego problemu. Jakieś sugestie?

+2

http://docs.oracle.com/javase/tutorial/java/javaOO/enum.html –

+0

Podobne pytanie tutaj może pomóc: http://stackoverflow.com/questions/1741708/enum-values-length-vs -prywatne pole – Parappa

+0

Nie ma powodu, aby mieć tablicę wszystkich wyliczeń w pierwszej kolejności - wyliczenia java są jedną z jego najlepszych funkcji, które zawsze niestety brakuje w C++/C#, gdzie jest to po prostu syntaktyczny cukier wokół "int". Chociaż w tym konkretnym przypadku nie użyłbym enum na początek, ale klasa - wydaje się lepszą architekturą (choć może to zależeć od dokładnego użycia itd.) – Voo

Odpowiedz

6

"Klasyczny" java zawsze używany "statyczny końcowy int ANIMAL_CAT = 0;" w takich przypadkach.

JDK 1.5 wprowadzone "typ bezpieczny teksty stałe":

http://www.javapractices.com/topic/TopicAction.do?Id=1

Można teraz ten (bardzo powszechna praktyka) zrobić:

enum Quark { 
    /* 
    * These are called "enum constants". 
    * An enum type has no instances other than those defined by its 
    * enum constants. They are implicitly "public static final". 
    * Each enum constant corresponds to a call to a constructor. 
    * When no args follow an enum constant, then the no-argument constructor 
    * is used to create the corresponding object. 
    */ 
    UP, 
    DOWN, 
    CHARM, 
    STRANGE, 
    BOTTOM, 
    TOP 
    } 

Albo można to zrobić:

/** 
    * Example 2 - adding a constructor to an enum. 
    * 
    * If no constructor is added, then the usual default constructor 
    * is created by the system, and declarations of the 
    * enum constants will correspond to calling this default constructor. 
    */ 
    public enum Lepton { 
    //each constant implicity calls a constructor : 
    ELECTRON(-1, 1.0E-31), 
    NEUTRINO(0, 0.0); 

    /* 
    * This constructor is private. 
    * Legal to declare a non-private constructor, but not legal 
    * to use such a constructor outside the enum. 
    * Can never use "new" with any enum, even inside the enum 
    * class itself. 
    */ 
    private Lepton(int aCharge, double aMass){ 
     //cannot call super ctor here 
     //calls to "this" ctors allowed 
     fCharge = aCharge; 
     fMass = aMass; 
    } 
    final int getCharge() { 
     return fCharge; 
    } 
    final double getMass() { 
     return fMass; 
    } 
    private final int fCharge; 
    private final double fMass; 
    } 

Oto oficjalna dokumentacja:

http://docs.oracle.com/javase/1.5.0/docs/guide/language/enums.html

+1

+1 dla wyboru przykładu. Ale powinieneś uważać, ponieważ 'Lepton.NEUTRINO' może podróżować szybciej niż światło. – yshavit

+0

Ten facet pracuje w CERN, po prostu to wiem. – st0le

4

Dlaczego nie po prostu:

public enum Animal { 
    CAT(4), RAT(4), BAT(4), CHICKEN(2), ..., ANT(6), SPIDER(8); 

    // A simple attribute, like any other java class. 
    // Final to ensure it will never change. 
    private final int legs; 

    // Note that this constructor is private. 
    private Animal(int legs) { 
     this.legs = legs; 
    } 

    // A method, like any other class. 
    public int getLegs() { 
     return legs; 
    } 
} 

Aby go użyć:

// How to get all the animals in the enum. 
Animal[] animals = Animal.values(); 

// How to get the number of animals. 
int numAnimals = animals.length; 

// How to get the number of legs of some animal. 
int howManyLegsHaveTheAnt = Animal.ANT.getLegs(); 
int howManyLegsHaveTheFirstAnimal = animals[0].getLegs(); 

// How to get the name of a given animal. 
String theFirstAnimalName = animals[0].name(); // Will return "CAT". 

// How to get the position in the enum of some animal. 
int antPositionInTheEnum = Animal.ANT.ordinal(); 

// How to get an animal given its name. 
Animal rat = Enum.valueOf(Animal.class, "RAT"); 
Animal someAnimal = Enum.valueOf(Animal.class, animalName); 
+0

Dziękuję za odpowiedź, ale mogłem nawiązywać do większej prostoty, niż zamierzałem. – jbeck

+0

Java to gadatliwy język. Prostota w java nie oznacza małego kodu. Uwierzcie, ten kod jest prosty. Co więcej, przeczytałem twoją odpowiedź na pytanie, które zmieniło ją w bardzo odmienne pytanie, więc opublikuję kolejną odpowiedź. –

1

W twoim przypadku, wykorzystując teksty stałe nie jest dobrym pomysłem. Wyliczenia są z natury rzeczy niezmiennymi, ustalonym zbiorem, w którym nie można wynaleźć żadnych nowych elementów i nie można zniszczyć istniejących elementów. Dni powszednie, miesiące roku i kombinacje kart są dobrym przykładem wyliczenia, istniejący zestaw jest stały i niezmienny, żaden nowy element nigdy nie zostanie stworzony i żaden istniejący element nigdy nie zostanie zniszczony. Gatunki zwierząt, gatunki muzyczne, zestawy klocków itp. Nie przestrzegają tych zasad.

Lepsze jest najbardziej oczywiste: Zdefiniuj interfejs lub klasę abstrakcyjną Animal. I definiuje wszystkie typy zwierząt jako podtyp.

Przełączniki są tak złe, jak goto, są proste, łatwe w użyciu, wyglądają niewinnie, ale w końcu przekształcają kod w spaghetti w dżungli. Zamiast używać przełączników, które chcesz, użyj polimorfizmu. Tj:

// Don't: 
public class SomeClass { 
    public void myMethod() { 
     switch (animal.id) { 
      case TYPE_CAT: 
       doSomethingThatCatDoes(); 
       doSomethingElse(); 
       anotherThing(); 
       break; 

      case TYPE_DOG: 
       bark(); 
       doSomethingThatDogDoes(); 
       otherThing(); 
       break; 
     } 
    } 
} 
// Do: 
public interface Animal { 
    public void myMethodAction(); 
    // Other methods. 
} 

public class Cat implements Animal { 
    @Override 
    public void myMethodAction() { 
     doSomethingThatCatDoes(); 
     doSomethingElse(); 
     anotherThing(); 
    } 
} 

public class Dog implements Animal { 
    @Override 
    public void myMethodAction() { 
     bark(); 
     doSomethingThatDogDoes(); 
     otherThing(); 
    } 
} 

public class SomeClass { 
    public void myMethod() { 
     animal.myMethodAction(); 
    } 
} 

Kod zwykle stają się większe, kiedy to zrobić, ale to nie oznacza bardziej skomplikowane. Zamiast tego, stała się lepiej zorganizowana, ponieważ SomeClass nie musi już znać dokładnego typu zwierzęcia. Logika charakterystyczna dla każdego rodzaju zwierząt jest w jednym tylko miejscu, a jeśli chcesz zmienić zachowanie zwierzęcia, wygrałeś ". T trzeba szukać i zmieniać wiele bardzo odległych miejsc. Ponadto znacznie łatwiej jest dodawać nowe typy zwierząt. Jak widać, Twój projekt będzie bardziej elastyczny pod względem przyszłych zmian (łatwiejszych do zmiany, trudniejszych do złamania).

Należy zauważyć, że nie użyłem jawnych identyfikatorów. Unikaj ids, ponieważ są one błędne i już masz ich implikację. Każdy obiekt ma unikalny adres pamięci, a adres pamięci jest identyfikatorem. Tak więc twoje pole public int mType; stanie się public Class<? extends Animal> mType;. Jeśli naprawdę potrzebujesz tych identyfikatorów, spraw, aby były możliwie jak najkrótsze i rzadziej używane. Powodem tego jest użycie OO beter (wyjaśnię to poniżej).

Co więcej, twój TypeInfo to tylko kilka publicznych atrybutów. Proszę, nigdy - nigdy - nigdy - nie myśl o tym. W OO atrybuty publiczne są tak samo nienawistne jak przymiarki i przełączniki, a NAJCZĘŚCIEJ ZABRANIA SIĘ 100% PRAWDOPODOBIEŃSTWA, które podważają twój projekt. Zamiast tego, cokolwiek robisz, aby obsłużyć pola, wpisz klasę TypeInfo i uczyń pola prywatnymi. Nie oznacza to zastępowania atrybutów publicznych przez pozbawione mózgu programy pobierające i ustawiające, zamiast tego umieszczaj tam swoją logikę biznesową.

Gorąco polecam pogłębić niektóre OO.W szczególności prawdziwa enkapsulacja (niektórzy ludzie twierdzą, że dodanie garstki pobierających mózg seterów jest w porządku, ale tak nie jest, to nie jest prawdziwa enkapsulacja). Studiuj polimorfizm, nie tylko to, czym on jest i jak działa, ale także jak z niego korzystać i dlaczego. A co najważniejsze, spójność badań i sprzężenie, aby zajęcia były sensowne, dość niezależne, wielokrotnego użytku i elastyczne.

0

Prosta odpowiedź: żadna Java nie ma wyrażeń typu C. Chociaż to samo można osiągnąć w Javie z serią instrukcji jak poniżej:

public static final int GREEN = 1; 
public static final int RED = 2; 
public static final int BLUE = 3; 

Wyliczenia Java są różne.