Cast Int to enum in Java
What is the correct way to cast an Int to an enum in Java given the following enum?
public enum MyEnum
{
EnumValue1,
EnumValue2
}
MyEnum enumValue = (MyEnum) x; //Doesn't w开发者_运维问答ork???
Try MyEnum.values()[x]
where x
must be 0
or 1
, i.e. a valid ordinal for that enum.
Note that in Java enums actually are classes (and enum values thus are objects) and thus you can't cast an int
or even Integer
to an enum.
MyEnum.values()[x]
is an expensive operation. If the performance is a concern, you may want to do something like this:
public enum MyEnum {
EnumValue1,
EnumValue2;
public static MyEnum fromInteger(int x) {
switch(x) {
case 0:
return EnumValue1;
case 1:
return EnumValue2;
}
return null;
}
}
If you want to give your integer values, you can use a structure like below
public enum A
{
B(0),
C(10),
None(11);
int id;
private A(int i){id = i;}
public int GetID(){return id;}
public boolean IsEmpty(){return this.equals(A.None);}
public boolean Compare(int i){return id == i;}
public static A GetValue(int _id)
{
A[] As = A.values();
for(int i = 0; i < As.length; i++)
{
if(As[i].Compare(_id))
return As[i];
}
return A.None;
}
}
You can try like this.
Create Class with element id.
public Enum MyEnum {
THIS(5),
THAT(16),
THE_OTHER(35);
private int id; // Could be other data type besides int
private MyEnum(int id) {
this.id = id;
}
public static MyEnum fromId(int id) {
for (MyEnum type : values()) {
if (type.getId() == id) {
return type;
}
}
return null;
}
}
Now Fetch this Enum using id as int.
MyEnum myEnum = MyEnum.fromId(5);
I cache the values and create a simple static access method:
public static enum EnumAttributeType {
ENUM_1,
ENUM_2;
private static EnumAttributeType[] values = null;
public static EnumAttributeType fromInt(int i) {
if(EnumAttributeType.values == null) {
EnumAttributeType.values = EnumAttributeType.values();
}
return EnumAttributeType.values[i];
}
}
Java enums don't have the same kind of enum-to-int mapping that they do in C++.
That said, all enums have a values
method that returns an array of possible enum values, so
MyEnum enumValue = MyEnum.values()[x];
should work. It's a little nasty and it might be better to not try and convert from int
s to Enum
s (or vice versa) if possible.
This not something that is usually done, so I would reconsider. But having said that, the fundamental operations are: int --> enum using EnumType.values()[intNum]
, and enum --> int using enumInst.ordinal()
.
However, since any implementation of values()
has no choice but to give you a copy of the array (java arrays are never read-only), you would be better served using an EnumMap
to cache the enum --> int mapping.
Use MyEnum enumValue = MyEnum.values()[x];
Here's the solution I plan to go with. Not only does this work with non-sequential integers, but it should work with any other data type you may want to use as the underlying id for your enum values.
public Enum MyEnum {
THIS(5),
THAT(16),
THE_OTHER(35);
private int id; // Could be other data type besides int
private MyEnum(int id) {
this.id = id;
}
public int getId() {
return this.id;
}
public static Map<Integer, MyEnum> buildMap() {
Map<Integer, MyEnum> map = new HashMap<Integer, MyEnum>();
MyEnum[] values = MyEnum.values();
for (MyEnum value : values) {
map.put(value.getId(), value);
}
return map;
}
}
I only need to convert id's to enums at specific times (when loading data from a file), so there's no reason for me to keep the Map in memory at all times. If you do need the map to be accessible at all times, you can always cache it as a static member of your Enum class.
In case it helps others, the option I prefer, which is not listed here, uses Guava's Maps functionality:
public enum MyEnum {
OPTION_1(-66),
OPTION_2(32);
private int value;
private MyEnum(final int value) {
this.value = value;
}
public int getValue() {
return this.value;
}
private static ImmutableMap<Integer, MyEnum> reverseLookup =
Maps.uniqueIndex(Arrays.asList(MyEnum.values())), MyEnum::getValue);
public static MyEnum fromInt(final int id) {
return reverseLookup.getOrDefault(id, OPTION_1);
}
}
With the default you can use null
, you can throw IllegalArgumentException
or your fromInt
could return an Optional
, whatever behavior you prefer.
Based on @ChadBefus 's answer and @shmosel comment, I'd recommend using this. (Efficient lookup, and works on pure java >= 8)
import java.util.stream.Collectors;
import java.util.function.Function;
import java.util.Map;
import java.util.Arrays;
public enum MyEnum {
OPTION_1(-66),
OPTION_2(32);
private int value;
private MyEnum(final int value) {
this.value = value;
}
public int getValue() {
return this.value;
}
private static Map<Integer, MyEnum> reverseLookup =
Arrays.stream(MyEnum.values()).collect(Collectors.toMap(MyEnum::getValue, Function.identity()));
public static MyEnum fromInt(final int id) {
return reverseLookup.getOrDefault(id, OPTION_1);
}
public static void main(String[] args) {
System.out.println(fromInt(-66).toString());
}
}
You can iterate over values()
of enum and compare integer value of enum with given id
like below:
public enum TestEnum {
None(0),
Value1(1),
Value2(2),
Value3(3),
Value4(4),
Value5(5);
private final int value;
private TestEnum(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public static TestEnum getEnum(int value){
for (TestEnum e:TestEnum.values()) {
if(e.getValue() == value)
return e;
}
return TestEnum.None;//For values out of enum scope
}
}
And use just like this:
TestEnum x = TestEnum.getEnum(4);//Will return TestEnum.Value4
I hope this helps ;)
Wrote this implementation. It allows for missing values, negative values and keeps code consistent. The map is cached as well. Uses an interface and needs Java 8.
Enum
public enum Command implements OrdinalEnum{
PRINT_FOO(-7),
PRINT_BAR(6),
PRINT_BAZ(4);
private int val;
private Command(int val){
this.val = val;
}
public int getVal(){
return val;
}
private static Map<Integer, Command> map = OrdinalEnum.getValues(Command.class);
public static Command from(int i){
return map.get(i);
}
}
Interface
public interface OrdinalEnum{
public int getVal();
@SuppressWarnings("unchecked")
static <E extends Enum<E>> Map<Integer, E> getValues(Class<E> clzz){
Map<Integer, E> m = new HashMap<>();
for(Enum<E> e : EnumSet.allOf(clzz))
m.put(((OrdinalEnum)e).getVal(), (E)e);
return m;
}
}
In Kotlin:
enum class Status(val id: Int) {
NEW(0), VISIT(1), IN_WORK(2), FINISHED(3), CANCELLED(4), DUMMY(5);
companion object {
private val statuses = Status.values().associateBy(Status::id)
fun getStatus(id: Int): Status? = statuses[id]
}
}
Usage:
val status = Status.getStatus(1)!!
A good option is to avoid conversion from int
to enum
: for example, if you need the maximal value, you may compare x.ordinal() to y.ordinal() and return x or y correspondingly. (You may need to re-order you values to make such comparison meaningful.)
If that is not possible, I would store MyEnum.values()
into a static array.
This is the same answer as the doctors but it shows how to eliminate the problem with mutable arrays. If you use this kind of approach because of branch prediction first if will have very little to zero effect and whole code only calls mutable array values() function only once. As both variables are static they will not consume n * memory for every usage of this enumeration too.
private static boolean arrayCreated = false;
private static RFMsgType[] ArrayOfValues;
public static RFMsgType GetMsgTypeFromValue(int MessageID) {
if (arrayCreated == false) {
ArrayOfValues = RFMsgType.values();
}
for (int i = 0; i < ArrayOfValues.length; i++) {
if (ArrayOfValues[i].MessageIDValue == MessageID) {
return ArrayOfValues[i];
}
}
return RFMsgType.UNKNOWN;
}
enum MyEnum {
A(0),
B(1);
private final int value;
private MyEnum(int val) {this.value = value;}
private static final MyEnum[] values = MyEnum.values();//cache for optimization
public static final getMyEnum(int value) {
try {
return values[value];//OOB might get triggered
} catch (ArrayOutOfBoundsException e) {
} finally {
return myDefaultEnumValue;
}
}
}
精彩评论