2012-01-03 25 views
5

Sto cercando di trovare un equivalente Java per la seguente convenienza in C++:C-like enum in Java

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

Animal animals[NUM_ANIMALS]; 

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

So che questa non è la cosa più bella del mondo, ma posso aggiungere una nuovo ANIMAL_xxx in qualsiasi punto dell'enum e tutte le voci seguenti verranno regolate automaticamente. C'è un modo pulito per farlo in Java?


Grazie per le risposte, ma potrei aver accennato a una semplicità maggiore di quanto intendessi.

Sto lavorando a un gioco, dove c'è una fisica, un motore di intelligenza artificiale, ecc. Sto cercando di consolidare tutte le informazioni di cui ho bisogno per ogni tipo di entità (alcuni hanno solo una manifestazione fisica, mentre alcuni hanno entrambi fisica e intelligenza artificiale, ecc.), in modo che io possa facilmente aggiungere/modificare i tipi (quindi, l'animale è un cattivo esempio, perché potrei aver bisogno di rocce, piante, ecc ...).

Sto usando una classe per memorizzare le informazioni sul tipo, mentre altre classi si basano su quella classe per cercare gli attributi (dimensioni, indice bitmap, hasAi, ecc ...) Alla fine, sto provando a pulire qualcosa di simile alla seguente, mentre che mi permette di aggiungere facilmente nuovi tipi:

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[]; 
} 

Ora, questo sembra come una sorta di implementazione XML potrebbe essere la cosa 'giusta' da fare, ma non sono sicuro su come farlo (nuovo in Java). In ogni caso, le altre classi possono facilmente usare unitTypeInfo.mTypes [UnitTypeInfo.TYPE_CAT] .mMass (istanziato) per cercare la massa del gatto. Tuttavia, se voglio aggiungere un TYPE_DOG sotto la definizione TYPE_CAT, devo aggiornare tutto sotto (pigro, lo so, ma immaginiamo che ci siano altri tipi.)

Enums offrono una soluzione facile a questo problema in C++, ma in Java, la soluzione più semplice con cui posso fare ora richiede qualcosa come: unitTypeInfo.mTypes [UnitTypeInfo.mTypeEnum.TYPE_CAT.ordinal()]. ​​mMass - e anche se suppongo che questo non sia molto più la mia soluzione già scadente, aggiunge un po 'più di indirezione e una chiamata al metodo reale (a un metodo che la maggior parte delle fonti non sembra incoraggiare).

Un'altra cosa è che voglio essere in grado di chiamare uno switch (tipo), in modo da limitare anche le possibili soluzioni.

In ogni caso, sto iniziando a pensare che ci sia una soluzione molto migliore a questo problema. Eventuali suggerimenti?

+2

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

+0

La domanda correlata qui potrebbe aiutare: http://stackoverflow.com/questions/1741708/enum-values-length-vs -privato-campo – Parappa

+0

Nessun motivo per avere una matrice di tutte le enumerazioni in primo luogo - le enumerazioni di java sono una delle sue migliori caratteristiche che mi manca sempre in C++/C# dove è il suo giusto zucchero sintattico attorno a un "int". Anche se in questo caso particolare non userei un enum per cominciare, ma una classe - mi sembra l'architettura migliore (anche se potrebbe dipendere dall'uso esatto e così via ..) – Voo

risposta

6

"Classico" java sempre utilizzato "statico finale int ANIMAL_CAT = 0;" in casi come questo

JDK 1.5 introdotte "enumerazioni type-safe":

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

È ora possibile fare questo (una pratica molto comune):

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 
    } 

Oppure si può fare questo:

/** 
    * 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; 
    } 

Ecco la documentazione ufficiale:

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

+1

+1 per la scelta dell'esempio. Ma dovresti stare attento, dato che 'Lepton.NEUTRINO' potrebbe viaggiare più veloce della luce. – yshavit

+0

Questo ragazzo lavora al CERN, lo so. – st0le

4

Perché non semplicemente questo:

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; 
    } 
} 

usarlo:

// 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

Grazie per la risposta, ma potrei aver accennato ad una semplicità maggiore di quanto intendessi. – jbeck

+0

Java è un linguaggio prolisso. La semplicità in Java non significa piccolo codice. Credici, questo codice è semplice. Inoltre, ho letto la tua modifica alla domanda, che l'ha trasformata in una domanda molto diversa, quindi pubblicherò un'altra risposta per questo. –

1

Nel tuo caso, utilizzando le enumerazioni non è una buona idea. Gli Enum sono cose intrinsecamente immutabili, un insieme fisso in cui non si possono inventare nuovi elementi e nessun elemento esistente può essere distrutto. I giorni della settimana, i mesi dell'anno e i semi delle carte sono un buon esempio di enumerazione, l'insieme esistente è fisso e immutabile, nessun nuovo elemento verrà mai creato e nessun elemento esistente sarà mai distrutto. Tipi di animali, generi musicali, tileset, ecc. Non segue queste regole.

Il migliore è il più ovvio: definire un'interfaccia o una classe astratta Animale. E definisce tutti i tipi di animali come sottotipizzazione.

Gli interruttori sono male come goto, sono semplici, sono facili da usare, sembrano innocenti, ma alla fine trasforma il tuo codice in una giungla di spaghetti. Invece di usare gli interruttori che desideri, usa il polimorfismo. Cioè:

// 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(); 
    } 
} 

Il codice tende a diventare più grandi quando si esegue questa operazione, ma questo non significa più complessa. Invece, è diventato meglio organizzato, perché la classe SomeClass non ha più bisogno di conoscere l'esatto tipo di animale, La logica particolare per ogni tipo di animale è tutto in un unico posto, e se hai bisogno di cambiare il comportamento di un animale, hai vinto ' Ho bisogno di cercare e cambiare un sacco di posti molto lontani. Inoltre, diventa molto più facile aggiungere nuovi tipi di animali. Come dovresti vedere, il tuo progetto diventa più flessibile per i cambiamenti futuri (vale a dire, più facile da cambiare, più difficile da interrompere).

Si noti che non ho utilizzato ID espliciti. Evita gli ID, perché sono cattivi e tu li hai già impliciti. Ogni oggetto ha un indirizzo di memoria univoco e quell'indirizzo di memoria è l'id. Quindi, il tuo campo public int mType; diventerebbe un public Class<? extends Animal> mType;. Se hai davvero bisogno di questi id, rendili il più breve possibile e il meno usato possibile. La ragione di questo è usare OO beter (lo spiegherò qui sotto).

Di più, il tuo TypeInfo è solo un mucchio di attributi pubblici. Per favore, mai-mai-mai pensare di fare questo. In OO, gli attributi pubblici sono odiosi quanto i gotos e gli switch, e ESSI SARANNO SICURAMENTE AL 100% DI PROBABILITÀ minando il tuo progetto. Invece, qualsiasi cosa tu debba fare per gestire i campi, inserisci la classe TypeInfo e rendi privati ​​i campi. Questo non significa sostituire gli attributi pubblici con getter e setter cerebrali, ma mettere la tua logica aziendale lì.

Consiglio vivamente di approfondire alcuni OO.Incapsulamento specificamente vero (alcune persone dicono che aggiungere un gruppo di getter e setter cerebrali è ok, ma non lo è, non è vero incapsulamento). Studia il polimorfismo, non solo quello che è e come funziona, ma come puoi usarlo e perché. E il più importante, studiare coesione e accoppiamento, per creare classi che abbiano un senso, abbastanza indipendenti, riutilizzabili e flessibili.

0

Risposta semplice: No Java non ha enumerazioni simili a C. Anche se lo stesso può essere ottenuto in java con una serie di affermazioni come di seguito:

Le enumerazioni Java sono diverse.