Setting an enum by int value [duplicate] - java

This question already has answers here:
Getting enum associated with int value
(8 answers)
Closed 5 years ago.
I have an enum with an int value for storage in a DB:
public enum DayType {
REGULAR(1), VACATION(2), SICK(3);
private final int value;
private DayType(int value)
{
this.value = value;
}
public int getValue() {
return value;
}
}
And here is a setter for this enum:
private DayType dayType
public void setDayType(int dayType) {
switch(dayType) {
case 1:
this.dayType = DayType.REGULAR;
break;
case 2:
this.dayType = DayType.VACATION;
break;
case 3:
this.dayType = DayType.SICK;
break;
default :
this.dayType = DayType.REGULAR;
break;
}
Everything works just fine. But there has to be a more "pretty" way to write the setter if there is a int value for each enum value. Without using switch case...

In this particular case you don't need to explicitly specify int value for each element, but just treat its constants as array:
private DayType dayType = DayType.REGULAR;
public void setDayType(int dayType) {
if (dayType <= DayType.values().length) this.dayType = DayType.values()[dayType - 1];
}

The usual way is to add a static method in the enum
public enum DayType {
REGULAR(1), VACATION(2), SICK(3);
private final int value;
private DayType(int value)
{
this.value = value;
}
public int getValue() {
return value;
}
public static DayType byValue(int value) {
Arrays.stream(DayType.values())
.filter(dt -> dt.value == value)
.findFirst()
.orElse(DayType.REGULAR);
}
}
Usage
private DayType dayType
public void setDayType(int dayType) {
this.dayType = DayType.byValue(dayType);
}

You can use the values of .ordinal() and values[index] to acess the values.
Instead of having the value field in the enum, store its DayType.REGULAR.ordinal() value.
So to load:
this.dayTipe = DayType.values[dayType];
The only downside here is to update the Enum, you need to add all new ones after the last one, so its indexes never conflicts.

You can avoid use of switch statement by using
public void setDayType(int type) {
for(DayType dayType : DayType.values()) {
if(dayType.getValue() == type) {
this.dayType = dayType;
}
}
}
If you needed performance rather than memory you can use HashMap:
private HashMap<Integer, DayType> dayTypes;
private void initDayTypes() {
for(DayType type : DayType.values()) {
dayTypes.put(type.value, type);
}
}
Usage will be the next:
public void setDayType(int type) {
this.dayType = dayTypes.get(type);
}

Related

Enum with negative/gap value in Java

I have a problem with enum in Java. I have an enum that starts from -1:
public enum AipType {
Unknown(-1),
None(0),
AipMod(1),
AipNoMod(2);
private final int id;
AipType(int id) {this.id = id;}
public int getValue() {return id;}
}
The problem is when I use this code to initialize a var of AipType
AipType at = AipType.getValues()[index];
where index is a number in the interval [-1,0,1,2] the -1 mess up the value.
i.e. 0 returns Unknown, 1 returns AipMod and 2 returns AipNoMod.
I used this implementation because I need to set manually the numeric value for each enum case. In other case I have a gap beetwen the values so I have the same problem: I cannot use values() and then access with [ ].
I tried to initialize in this way
AipType at = AipType(index);
but doesn't work.
Ideas ? Thanks...
We don't know what the getValues() method you're using exactly doing. Is it supposed to be values().
Anyway, you can always add a static method in your enum, which returns the correct enum instance for that value, and invoke it wherever you need it:
public enum AipType {
Unknown(-1),
None(0),
AipMod(1),
AipNoMod(2);
private final int id;
AipType(int id) {this.id = id;}
public int getValue() {return id;}
public static AipType fromValue(int id) {
for (AipType aip: values()) {
if (aip.getValue() == id) {
return aip;
}
}
return null;
}
}
If you're invoking fromValue() too often, you might also want to cache the array returned by values() inside the enum itself, and use it. Or even better, a map would be a better idea.
Enum.getValues() returns an array of the enums based on the definition order in the enum class.
getValues() doesn't know about the id field or the getValue() method you have added to your enum.
What you could do instead of calling getValues()[-1] (by the way, you'll never be able to index an array in Java with -1) is to add a static function like:
static AipType getAipType(int id) {
for (AipType a : getValues()) {
if (a.getId() == id) return a;
}
throw new IllegalArgumentException("id=" + id + " does not exist");
}
Just have a Map<Integer, AipType> instead of using values(), and expose access to it via a method:
public enum AipType {
UNKNOWN(-1),
NONE(0),
MOD(1),
NO_MOD(2);
private static final Map<Integer, AipType> VALUE_TO_ENUM_MAP;
private final int value;
static {
VALUE_TO_ENUM_MAP = new HashMap<>();
for (AipType type : EnumSet.allOf(AipType.class)) {
VALUE_TO_ENUM_MAP.put(type.value, type);
}
}
private AipType(int value) {
this.value = value;
}
public int getValue() {
return id;
}
public static AipType forValue(int value) {
return VALUE_TO_ENUM_MAP.get(value);
}
}
That will be completely flexible about values - or you could still use an array and just offset it appropriately.
Can you declare your enum like below?
public enum AipType {
Unknown(-1),None(0),AipMod(1),AipNoMod(2);
private int value;
private AipType(int value) {
this.value = value;
}
public int getValue() {
return this.value;
}
public static AipType fromValue(int value) {
for (AipType at: values()) {
if (at.getValue() == value) {
return at;
}
}
return null;
}
};
And instantiate like:
AipType at = AipType.fromValue(-1);
If the ids are completely custom, the only chance you'd have would be to create a map and store the id->AipType mapping there.
Example:
public enum AipType {
... //enum definitions here
static Map<Integer, AipType> map = new HashMap<>();
static {
for( AipType a : AipType.values() ) {
map.put(a.id, a);
}
}
public static AipType typeById( int id ) {
return map.get(id);
}
}
Then call it like AipType.typeById(-1);.

Get the name of enum based on the value [duplicate]

This question already has answers here:
Getting enum associated with int value
(8 answers)
Java getting the Enum name given the Enum Value
(7 answers)
Closed 9 years ago.
I have the following enum
public enum AppointmentSlotStatusType {
INACTIVE(0), ACTIVE(1);
private int value;
private AppointmentSlotStatusType(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public String getName() {
return name();
}
}
How do I get the enum name if a value is known for instance 1 ?
For this specific enum it's easy
String name = TimeUnit.values()[1].name();
You can implement a public static method inside the enum, which will give you the enum instance for that id:
public static AppointmentSlotStatusType forId(int id) {
for (AppointmentSlotStatusType type: values()) {
if (type.value == id) {
return value;
}
}
return null;
}
Probably you would also like to cache the array returned by values() in a field:
public static final AppointmentSlotStatusType[] VALUES = values();
then use VALUES instead of values().
Or you can use a Map instead.
private static final Map<Integer, AppointmentSlotStatusType> map = new HashMap<>();
static {
for (AppointmentSlotStatusType type: values()) {
map.put(type.value, type);
}
}
public static AppointmentSlotStatusType forId(int id) {
return map.get(id);
}
You can maintain a Map to hold name for Integer key.
public enum AppointmentSlotStatusType {
INACTIVE(0), ACTIVE(1);
private int value;
private static Map<Integer, AppointmentSlotStatusType> map = new HashMap<Integer, AppointmentSlotStatusType>();
static {
for (AppointmentSlotStatusType item : AppointmentSlotStatusType.values()) {
map.put(item.value, item);
}
}
private AppointmentSlotStatusType(final int value) { this.value = value; }
public static AppointmentSlotStatusType valueOf(int value) {
return map.get(value);
}
}
Take a look at this answer.

can not define int variable in enum - java

i try to define enum with int in it, but i have error in eclipse : "Syntax error on token "int", delete this token"
my code:
package util.enumurations;
public enum BooleanEnum
{
private int value;
static
{
BooleanEnum[] arrayOfBooleanEnum = new BooleanEnum[2];
arrayOfBooleanEnum[0] = False;
arrayOfBooleanEnum[1] = True;
}
private BooleanEnum(int arg3)
{
int j;
this.value = j;
}
public int getValue()
{
return this.value;
}
}
The first thing in an enum have to be the declaration of the possible values.
public enum BooleanEnum
{
False(0), True(1);
private final int value;
static
{
BooleanEnum[] arrayOfBooleanEnum = new BooleanEnum[2];
arrayOfBooleanEnum[0] = False;
arrayOfBooleanEnum[1] = True;
}
private BooleanEnum(int arg3)
{
this.value = arg3;
}
public int getValue()
{
return this.value;
}
}
Use
java.lang.Boolean.TRUE,
java.lang.Boolean.FALSE
instead
Actually The body of an enum type may contain enum constants. An enum constant defines an instance of the enum type.
What you are trying to is You aren't gonna need it.
Just use a simple Boolean almost it self acts as ENUM for true false types.
Use Boolean.valueOf();

Replace String Literals If/elseIf block with Enum

I'm new to using Java Enums and I've read that replace IF logic that compares String literals should be replaced with an Enum. I don't quite understand how to replace my below code with an Enum, any ideas? Based on the col value being passed into applyEQ, I need to do a base the next method call on it's value. I do know the possible values of col ahead of time and I'm using a constants file for now. Should I create an Enum and place it in my Interface of Constants file?
public class FilterHelper implements IFilterHelper {
private final EQuery eQuery;
public FilterHelper(EQuery query) {
eQuery = query;
}
#Override
public void applyEQ(String col, String val) throws Exception {
int return = 0;
if (col.equalsIgnoreCase(EConstants.NAME)) {
ret = Sample.addName(eQuery, val);
} else if (col.equalsIgnoreCase(EConstants.KEYWORDS)) {
ret = Sample.addKey(eQuery, val);
} else if (col.equalsIgnoreCase(EConstants.ROLE)) {
ret = Sample.addRole(eQuery, val);
}
if (return != 0) {
throw new Exception("failed");
}
}
}
EConstants.java
public final class EConstants {
public static final String NAME = "cewName";
public static final String KEYWORDS = "cewKeywords";
public static final String ROLE = "cewRole";
}
First create an enum:
public enum EConstants {
CEWNAME,
CEWROLE,
CEWKEYWORDS;
}
Then convert col String to this enum and use switch:
public void applyEQ(String col, String val) throws Exception {
int ret = 0;
final EConstants constant = EConstants.valueOf(col.toUpperCase());
switch(constant) {
case CEWNAME:
ret = Sample.addName(eQuery, val);
break;
case CEWROLE:
ret = Sample.addRole(eQuery, val);
break;
case CEWKEYWORDS:
ret = Sample.addKey(eQuery, val);
break;
default:
throw new Exception("Unhandled enum constant: " + constant);
}
}
Note that EConstants.valueOf() can throw IllegalArgumentException if col.toUpperCase() does not match any of constant values.
BTW I hate local variables initialized in multiple places (and break keyword), try extracting method:
final EConstants constant = EConstants.valueOf(col.toUpperCase());
final int ret = processSample(val, constant);
And the method itself:
private int processSample(String val, EConstants constant) throws Exception {
switch(constant) {
case CEWNAME:
return Sample.addName(eQuery, val);
case CEWROLE:
return Sample.addRole(eQuery, val);
case CEWKEYWORDS:
return Sample.addKey(eQuery, val);
default:
throw new Exception("Unhandled enum constant: " + constant);
}
}
You can rewrite your EConstants as enum:
public enum EConstants {
NAME, KEYWORDS, ROLE
}
And evaluate condition using switch statement:
// col has type of EConstants
switch (col) {
case NAME:
// do something
break;
case KEYWORDS:
// do something
break;
case ROLE:
// do something
break;
default:
// what to do otherwise
break;
}
The great thing about Java Enums is that they provide language level support for the type safe enum pattern, because among other things it allows you to define methods and even override them. So you could do this:
public enum CewColumn {
NAME("cewName") {
#Override
public int add(EQuery eQuery, String val) {
return Sample.addName(eQuery, val);
}
},
KEYWORDS("cewKeywords") {
#Override
public int add(EQuery eQuery, String val) {
return Sample.addKey(eQuery, val);
}
},
ROLE("cewRole") {
#Override
public int add(EQuery eQuery, String val) {
return Sample.addRole(eQuery, val);
}
};
private final String colName;
private MyColumn(String colName) {
this.colName = colName;
}
private static final Map<String, CewColumn> COLUMNS = new HashMap<>(values().length);
static{
for (CewColumn cewColumn : values()){
COLUMNS.put(cewColumn.colName, cewColumn);
}
}
public abstract int add(EQuery eQuery, String val);
public static CewColumn getCewColumn(String colName){
return COLUMNS.get(colName);
}
}
Then you can use it like this:
CewColumn cewColumn = CewColumn.getCewColumn(colName);
if (cewColumn != null){
int ret = cewColumn.add(eQuery, val);
}
-> You replaced the switch statement with polymorphism!
it is best to create a Enum.
public Enum AvailableCols{
COL_1,
COL_2;
}
and convert the procedure as
public void applyEQ(AvailableCols col, String val) throws Exception {
switch(col){
case COL1:
...
If you still want the string to be preserved you can see the following post
Basically create an enum and change the type of col and use equals() or == to compare the value of col against the enum values. Alternatively you could use a switch statement but I doubt that would make your code more readable for only 3 constants.
Example:
enum EConstants {
NAME,
KEYWORDS,
ROLE;
}
public void applyEQ(EConstants col, String val) throws Exception {
if( col == EConstants.NAME ) {
...
}
....
}
//or
public void applyEQ(EConstants col, String val) throws Exception {
if( EConstants.NAME.equals(col) ) { //col might be null
...
}
....
}
//or
public void applyEQ(EConstants col, String val) throws Exception {
switch( col ) {
case NAME:
...
break;
case ROLE:
...
}
}
http://docs.oracle.com/javase/tutorial/java/javaOO/enum.html
If your raw data is a string, you will still need to do a string comparison to assign the enum. This might be faster if you do a lot of comparisons on the result data, but if not, it simply adds complication to your code.
You can iterate over the values of the enum like a collection, which gives you an advantage when you need to add constants. That's not bad.
Here is how to do it:
public enum EConstants {
NAME, KEYWORDS, ROLE
}
...
public EConstants setConstant(String from) {
if (from.equalsIgnoreCase("cewName")) {
return NAME;
} else if (col.equalsIgnoreCase("cewKeywords")) {
return KEYWORDS;
} else if (col.equalsIgnoreCase("cewRole")) {
return ROLE;
}
}
You preprocess your data that way and now when you are trying to figure out logic you can use a switch on the enum type value.
Here is a trick for you. No switch/case (just come up with a better name for EConstants).
public enum EConstants {
NAME,
KEYWORDS,
ROLE;
private interface Applier {
void apply(EQuery query, String val);
}
public void apply(EQuery query, String val) {
map.get(this).apply(query, val);
}
private static Map<EConstants, Applier> map = new HashMap<EConstants, EConstants.Applier>();
static {
map.put(NAME, new Applier() {
#Override
public void apply(EQuery query, String val) {
Sample.addName(query, val);
}
});
map.put(KEYWORDS, new Applier() {
#Override
public void apply(EQuery query, String val) {
Sample.addKey(query, val);
}
});
map.put(ROLE, new Applier() {
#Override
public void apply(EQuery query, String val) {
Sample.addRole(query, val);
}
});
}
}
Now you just write:
#Override
public void applyEQ(EConstants econs, String val) {
econs.apply(equery, val);
}

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 work???
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 ints to Enums (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;
}
}
}

Categories

Resources