Java中的C-like枚举

时间:2012-01-03 04:54:04

标签: java c++ arrays enums

我正在尝试在C ++中为以下方便找到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; ...

我知道这不是世界上最漂亮的东西,但我可以在枚举中的任何地方添加新的ANIMAL_xxx,以下所有条目都会自动调整。在Java中有一种干净的方法吗?


感谢您的回复,但我可能提到了比我想象的更简单。

我正在开发一款游戏,那里有一个物理,AI引擎等等。我正在尝试整合我需要的每种类型实体的所有信息(有些只有物理表现,有些只有物理表现和AI等...),以便我可以轻松添加/修改类型(因此,动物是一个不好的例子,因为我可能需要岩石,植物等......)。

我正在使用类来存储类型信息,而其他类依赖于该类来查找属性(维度,位图索引,hasAi等等)。最后,我正在尝试清理类似于以下内容,同时允许我轻松添加新类型:

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

现在,这似乎是某种XML实现可能是'正确'的事情,但我不确定如何做到这一点(Java新手)。无论如何,其他类可以很容易地使用unitTypeInfo.mTypes [UnitTypeInfo.TYPE_CAT] .mMass(实例化)来查找cat的质量。但是,如果我想在TYPE_CAT定义下面添加一个TYPE_DOG,我必须更新它下面的所有内容(懒惰,我知道,但是让我们知道还有更多类型。)

Enums在C ++中为这个问题提供了一个简单的解决方案,但在Java中,我现在能够解决的最简单的解决方案需要:unitTypeInfo.mTypes [UnitTypeInfo.mTypeEnum.TYPE_CAT.ordinal()]。mMass - and while我认为这并不比我已经糟糕的解决方案更糟糕,它确实增加了更多的间接性和实际的方法调用(对于大多数来源似乎不鼓励的方法)。

还有一件事是我希望能够调用一个开关(类型),这样也可能会限制可能的解决方案。

无论如何,我开始意识到必须有一个更好的解决方案来解决这个问题。有什么建议吗?

4 个答案:

答案 0 :(得分:7)

“Classic”java总是使用“static final int ANIMAL_CAT = 0;”在这种情况下。

JDK 1.5引入了“类型安全枚举”:

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

你现在可以这样做(一种非常普遍的做法):

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
  }

或者你可以这样做:

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

以下是官方文档:

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

答案 1 :(得分:5)

为什么不简单:

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

使用它:

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

答案 2 :(得分:1)

在您的情况下,使用枚举不是一个好主意。枚举本质上是不可变的东西,是一个固定的集合,不会发明任何新的元素,也不会破坏现有的元素。工作日,一年中的几个月和卡套装是枚举的一个很好的例子,现有的集合是固定的和不可变的,不会创建任何新元素,也不会销毁任何现有元素。动物类型,音乐类型,拼贴等等不符合这些规则。

最明显的是:定义一个接口或抽象类Animal。并将所有类型的动物定义为子类型。

开关和goto一样邪恶,它们很简单,易于使用,看起来很无辜,但最终会在意大利面丛林中转换你的代码。而不是使用您想要的开关,使用多态。即:

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

执行此操作时代码会变得更大,但这并不意味着更复杂。相反,它变得更有条理,因为SomeClass不再需要知道确切的动物类型,每种动物类型的特定逻辑都在一个地方,如果你需要改变动物的行为,你就赢了我需要搜索和改变很多很遥远的地方。此外,添加新的动物类型变得更加容易。正如您所看到的,您的项目对未来的更改更加灵活(即更容易更改,更难破解)。

您应该注意我没有使用显式ID。避免使用ID,因为它们是恶性的,你已经有了它们的隐含性。每个对象都有一个唯一的内存地址,该内存地址是id。因此,您的public int mType;字段将成为public Class<? extends Animal> mType;。如果您确实需要这些ID,请尽可能使它们尽可能短,并尽可能少使用。原因是使用OO beter(我将在下面解释)。

此外,您的TypeInfo只是一堆公共属性。请永远不要想到这样做。在面向对象中,公共属性与冒险和交换机一样令人讨厌,并且它们将以100%的概率破坏您的项目。相反,无论您需要处理哪些字段,都要输入TypeInfo类并将字段设为私有。这并不意味着用脑死亡的吸气剂和制定者取代公共属性,而是将你的业务逻辑放在那里。

我强烈建议你更深入地学习一些OO。特别是真正的封装(有些人说添加一堆脑死亡的getter和setter是可以的,但事实并非如此,这不是真正的封装)。研究多态性,不仅仅是它是什么以及如何工作,而是如何使用它以及为什么。最重要的是,研究凝聚力和耦合,使课程变得有意义,相当独立,可重复使用和灵活。

答案 3 :(得分:0)

简单回答:没有Java没有类似C的枚举。虽然在java中可以通过以下一系列语句实现相同的目的:

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

Java枚举不同。