122

When working with variables/parameters that can only take a finite number of values, I try to always use Java's enum, as in

public enum BonusType {
  MONTHLY, YEARLY, ONE_OFF
}

As long as I stay inside my code, that works fine. However, I often need to interface with other code that uses plain int (or String) values for the same purpose, or I need to read/write from/to a database where the data is stored as a number or string.

In that case, I'd like to have a convenient way to associate each enum value with a an integer, such that I can convert both ways (in other words, I need a "reversible enum").

Going from enum to int is easy:

public enum BonusType {
  public final int id;

  BonusType(int id) {
    this.id = id;
  }
  MONTHLY(1), YEARLY(2), ONE_OFF(3);
}

Then I can access the int value as BonusType x = MONTHLY; int id = x.id;.

However, I can see no nice way for the reverse, i.e. going from int to enum. Ideally, something like

BonusType bt = BonusType.getById(2); 

The only solutions I could come up with are:

  • Put a lookup method into the enum, which uses BonusType.values() to fill a map "int -> enum", then caches that and uses it for lookups. Would work, but I'd have to copy this method identically into each enum I use :-(.
  • Put the lookup method into a static utility class. Then I'd only need one "lookup" method, but I'd have to fiddle with reflection to get it to work for an arbitrary enum.

Both methods seem terribly awkward for such a simple (?) problem.

Any other ideas/insights?

sleske
  • 81,358
  • 34
  • 189
  • 227
  • 2
    I <3 java enums but hate them for this reason exactly! It always seems like they're perfect aside from one really ugly flaw... – Chris Thompson Feb 16 '11 at 19:46
  • 9
    for enum->int you can just use `ordinal()` – davin Feb 16 '11 at 19:48
  • 1
    Are your id-values decidable by you (meaning, couldn't you just use `.ordinal()`), or are they decided by outside forces? – Paŭlo Ebermann Feb 16 '11 at 19:57
  • 2
    @davin: Yes, and have your code break the moment someone rearranges the enum declaration, or deletes a value in the middle. I'm afraid that's not a robust solution :-/. – sleske Feb 16 '11 at 19:58
  • 1
    @davin using "ordinal()" should be avoided whenever possible, it's in the language's specification – DPM May 16 '13 at 12:12

18 Answers18

344

enum → int

yourEnum.ordinal()

int → enum

EnumType.values()[someInt]

String → enum

EnumType.valueOf(yourString)

enum → String

yourEnum.name()

A side-note:
As you correctly point out, the ordinal() may be "unstable" from version to version. This is the exact reason why I always store constants as strings in my databases. (Actually, when using MySql, I store them as MySql enums!)

aioobe
  • 413,195
  • 112
  • 811
  • 826
  • 2
    +1 This is the obvious correct answer. Note though, there is a single argument method for valueOf which takes only a String and exists as long as you are using the concrete enum type (e.g. `BonusType.valueOf("MONTHLY")`) – Tim Bender Feb 16 '11 at 19:57
  • 20
    Using `ordinal()` strikes me as a problematic solution, because it will break when the list of enum values is rearranged, or a value is deleted. Also, this is only practical if the int values are 0...n (which I have often found not to be the case). – sleske Feb 16 '11 at 20:00
  • @Tim Bender, Oh, right, thanks, updated. Thought it was there, but I didn't find it in the eclipse content assistant ;P – aioobe Feb 16 '11 at 20:00
  • 4
    @sleske, if you start deleting constants you're in trouble with existing persisted data anyway. (Updated my answer in this regard.) – aioobe Feb 16 '11 at 20:01
  • @aioobe: You do have a point. Still, if I for example purged my DB of all remaining obsolete enum IDs, I'd like to be able to remove the enum constant as well. – sleske Feb 16 '11 at 20:05
  • Then either use a sightly more sophisticated update statement, or store them as strings. That's the best recommendation I can give. – aioobe Feb 16 '11 at 20:09
  • @aioobe: For the record, I agree that storing enum values as strings is better. However, I often work with existing code (and DB schemas) that use int, and I can't always change this. – sleske Feb 16 '11 at 20:11
  • 3
    Using the `values()` array will only work if all your values are 0 indexed for their id and are declared in order. (I tested this to verify that if you declare `FOO(0), BAR(2), BAZ(1);` that `values[1] == BAR` and `values[2] == BAZ` despite the ids passed in .) – corsiKa Feb 16 '11 at 20:14
  • @sleske, Ah, I see how that could be a problem. Then I'd say, either arrange the constants so they get the same ordinal (adding dummy constants if padding is needed (they're not expensive to have around anyway, and may serve a documentation purpose to explain the "holes")) or go for a "custom" lookup-table. – aioobe Feb 16 '11 at 20:15
  • 2
    @glowcoder, well of-course, the integer argument is merely a field in the enum-object. It has nothing to do with the ordinal constant associated with the enum object (it could just as well have been a `double`). – aioobe Feb 16 '11 at 20:16
  • @aioobe Exactly the point. He is looking for a good way to have that "custom look-up table" without having to redefine that procedure for every enum he comes across. – corsiKa Feb 16 '11 at 21:02
  • What is **ordinal()**? – IgorGanapolsky Apr 13 '17 at 18:47
39

http://www.javaspecialists.co.za/archive/Issue113.html

The solution starts out similar to yours with an int value as part of the enum definition. He then goes on to create a generics-based lookup utility:

public class ReverseEnumMap<V extends Enum<V> & EnumConverter> {
    private Map<Byte, V> map = new HashMap<Byte, V>();
    public ReverseEnumMap(Class<V> valueType) {
        for (V v : valueType.getEnumConstants()) {
            map.put(v.convert(), v);
        }
    }

    public V get(byte num) {
        return map.get(num);
    }
}

This solution is nice and doesn't require 'fiddling with reflection' because it's based on the fact that all enum types implicitly inherit the Enum interface.

Jeff
  • 3,669
  • 1
  • 23
  • 33
  • Doesn't this use the ordinal? Sleske uses an id just because the ordinal changes when the enum values get reordered. – extraneon Feb 16 '11 at 19:52
  • No, it doesn't use ordinal. It relies on an explicitly defined int value. That int value is used as the map key (returned by v.convert()). – Jeff Feb 16 '11 at 19:57
  • 2
    I really like this solution; it seems this is the most general you can get. – sleske Feb 17 '11 at 14:05
  • +1. My only note is that I'd use `Number` instead of `Byte`, because my backing value might be larger in size. – Ivaylo Slavov Jan 04 '13 at 10:56
  • Really and truly look at the other answer with 183 upvotes (as of writing this comment). Some times Java can seem complicated enough to need something like this but this is not one of those cases! – James Mar 18 '14 at 23:12
  • 3
    Really and truly read the question. If you're dealing with a legacy database or external system that has defined integers that you don't want to propagate through your own code, then this is exactly one of those cases. The ordinal is an extremely fragile way to persist the value of an enum, and beyond that it is useless in the specific case mentioned in the question. – Jeff Mar 19 '14 at 16:06
33

I found this on the web, it was very helpful and simple to implement. This solution was NOT made by me

http://www.ajaxonomy.com/2007/java/making-the-most-of-java-50-enum-tricks

public enum Status {
 WAITING(0),
 READY(1),
 SKIPPED(-1),
 COMPLETED(5);

 private static final Map<Integer,Status> lookup 
      = new HashMap<Integer,Status>();

 static {
      for(Status s : EnumSet.allOf(Status.class))
           lookup.put(s.getCode(), s);
 }

 private int code;

 private Status(int code) {
      this.code = code;
 }

 public int getCode() { return code; }

 public static Status get(int code) { 
      return lookup.get(code); 
 }

}

melqkiades
  • 331
  • 3
  • 2
12

Seems the answer(s) to this question are outdated with the release of Java 8.

  1. Don't use ordinal as ordinal is unstable if persisted outside the JVM such as a database.
  2. It is relatively easy to create a static map with the key values.

public enum AccessLevel {
  PRIVATE("private", 0),
  PUBLIC("public", 1),
  DEFAULT("default", 2);

  AccessLevel(final String name, final int value) {
    this.name = name;
    this.value = value;
  }

  private final String name;
  private final int value;

  public String getName() {
    return name;
  }

  public int getValue() {
    return value;
  }

  static final Map<String, AccessLevel> names = Arrays.stream(AccessLevel.values())
      .collect(Collectors.toMap(AccessLevel::getName, Function.identity()));
  static final Map<Integer, AccessLevel> values = Arrays.stream(AccessLevel.values())
      .collect(Collectors.toMap(AccessLevel::getValue, Function.identity()));

  public static AccessLevel fromName(final String name) {
    return names.get(name);
  }

  public static AccessLevel fromValue(final int value) {
    return values.get(value);
  }
}
John Meyer
  • 121
  • 1
  • 3
  • Shouldn't the second parameter of `Collectors.toMap()` be `Functions.identity()` instead of `null`? – Adam Michalik Dec 02 '15 at 20:21
  • yes, I adopted this from a helper class I use with guava that converts null into identity. – John Meyer Dec 02 '15 at 21:44
  • That's a neat use of Java 8's new features. However, it still means the code would have to be repeated in every enum - and my question was about avoiding this (structurally) repeated boilerplate. – sleske Dec 02 '15 at 22:37
5

org.apache.commons.lang.enums.ValuedEnum;

To save me writing loads of boilerplate code or duplicating code for each Enum, I used Apache Commons Lang's ValuedEnum instead.

Definition:

public class NRPEPacketType extends ValuedEnum {    
    public static final NRPEPacketType TYPE_QUERY = new NRPEPacketType( "TYPE_QUERY", 1);
    public static final NRPEPacketType TYPE_RESPONSE = new NRPEPacketType( "TYPE_RESPONSE", 2);

    protected NRPEPacketType(String name, int value) {
        super(name, value);
    }
}

Usage:

int -> ValuedEnum:

NRPEPacketType packetType = 
 (NRPEPacketType) EnumUtils.getEnum(NRPEPacketType.class, 1);
Alastair McCormack
  • 26,573
  • 8
  • 77
  • 100
3

In this code, for permanent and intense search , have memory or process for use, and I select memory, with converter array as index. I hope it's helpful

public enum Test{ 
VALUE_ONE(101, "Im value one"),
VALUE_TWO(215, "Im value two");
private final int number;
private final byte[] desc;

private final static int[] converter = new int[216];
static{
    Test[] st = values();
    for(int i=0;i<st.length;i++){
        cv[st[i].number]=i;
    }
}

Test(int value, byte[] description) {
    this.number = value;
    this.desc = description;
}   
public int value() {
    return this.number;
}
public byte[] description(){
    return this.desc;
}

public static String description(int value) {
    return values()[converter[rps]].desc;
}

public static Test fromValue(int value){
return values()[converter[rps]];
}
}
Luís Cruz
  • 14,780
  • 16
  • 68
  • 100
3

You could perhaps use something like

interface EnumWithId {
    public int getId();

}


enum Foo implements EnumWithId {

   ...
}

That would reduce the need for reflection in your utility class.

Veger
  • 37,240
  • 11
  • 105
  • 116
extraneon
  • 23,575
  • 2
  • 47
  • 51
2

Both the .ordinal() and values()[i] are unstable since they are dependent to the order of enums. Thus if you change the order of enums or add/delete some your program would break.

Here is a simple yet effective method to map between enum and int.

public enum Action {
    ROTATE_RIGHT(0), ROTATE_LEFT(1), RIGHT(2), LEFT(3), UP(4), DOWN(5);

    public final int id;
    Action(int id) {
        this.id = id;
    }

    public static Action get(int id){
        for (Action a: Action.values()) {
            if (a.id == id)
                return a;
        }
        throw new IllegalArgumentException("Invalid id");
    }
}

Applying it for strings shouldn't be difficult.

hrzafer
  • 1,123
  • 1
  • 15
  • 35
  • Yes, I realize I could do that - or even better, use a map for the reverse lookup, rather than iterate through all values. I mentioned this in my question, and I also mentioned that I am looking for a better solution, to avoid having boilerplate code in each enum. – sleske May 10 '13 at 10:22
2

A very clean usage example of reverse Enum

Step 1 Define an interface EnumConverter

public interface EnumConverter <E extends Enum<E> & EnumConverter<E>> {
    public String convert();
    E convert(String pKey);
}

Step 2

Create a class name ReverseEnumMap

import java.util.HashMap;
import java.util.Map;

public class ReverseEnumMap<V extends Enum<V> & EnumConverter<V>> {
    private Map<String, V> map = new HashMap<String, V>();

    public ReverseEnumMap(Class<V> valueType) {
        for (V v : valueType.getEnumConstants()) {
            map.put(v.convert(), v);
        }
    }

    public V get(String pKey) {
        return map.get(pKey);
    }
}

Step 3

Go to you Enum class and implement it with EnumConverter<ContentType> and of course override interface methods. You also need to initialize a static ReverseEnumMap.

public enum ContentType implements EnumConverter<ContentType> {
    VIDEO("Video"), GAME("Game"), TEST("Test"), IMAGE("Image");

    private static ReverseEnumMap<ContentType> map = new ReverseEnumMap<ContentType>(ContentType.class);

    private final String mName;

    ContentType(String pName) {
        this.mName = pName;
    }

    String value() {
        return this.mName;
    }

    @Override
    public String convert() {
        return this.mName;
    }

    @Override
    public ContentType convert(String pKey) {
        return map.get(pKey);
    }
}

Step 4

Now create a Communication class file and call it's new method to convert an Enum to String and String to Enum. I have just put main method for explanation purpose.

public class Communication<E extends Enum<E> & EnumConverter<E>> {
    private final E enumSample;

    public Communication(E enumSample) {
        this.enumSample = enumSample;
    }

    public String resolveEnumToStringValue(E e) {
        return e.convert();
    }

    public E resolveStringEnumConstant(String pName) {
        return enumSample.convert(pName);
    }

//Should not put main method here... just for explanation purpose. 
    public static void main(String... are) {
        Communication<ContentType> comm = new Communication<ContentType>(ContentType.GAME);
        comm.resolveEnumToStringValue(ContentType.GAME); //return Game
        comm.resolveStringEnumConstant("Game"); //return GAME (Enum)
    }
}

Click for for complete explanation

Veger
  • 37,240
  • 11
  • 105
  • 116
AZ_
  • 21,688
  • 25
  • 143
  • 191
  • 1
    This I really, really like - I've been searching for a solid solution to this problem for some time now. The only change I made was to make `ContentType convert(String pKey)` static, which removes the need for the `Communication` class and was more to my liking. +1 – Chris Mantle Oct 30 '13 at 20:51
2

Use an interface to show it who's boss.

public interface SleskeEnum {
    int id();

    SleskeEnum[] getValues();

}

public enum BonusType implements SleskeEnum {


  MONTHLY(1), YEARLY(2), ONE_OFF(3);

  public final int id;

  BonusType(int id) {
    this.id = id;
  }

  public SleskeEnum[] getValues() {
    return values();
  }

  public int id() { return id; }


}

public class Utils {

  public static SleskeEnum getById(SleskeEnum type, int id) {
      for(SleskeEnum t : type.getValues())
          if(t.id() == id) return t;
      throw new IllegalArgumentException("BonusType does not accept id " + id);
  }

  public static void main(String[] args) {

      BonusType shouldBeMonthly = (BonusType)getById(BonusType.MONTHLY,1);
      System.out.println(shouldBeMonthly == BonusType.MONTHLY);

      BonusType shouldBeMonthly2 = (BonusType)getById(BonusType.MONTHLY,1);
      System.out.println(shouldBeMonthly2 == BonusType.YEARLY);

      BonusType shouldBeYearly = (BonusType)getById(BonusType.MONTHLY,2);
      System.out.println(shouldBeYearly  == BonusType.YEARLY);

      BonusType shouldBeOneOff = (BonusType)getById(BonusType.MONTHLY,3);
      System.out.println(shouldBeOneOff == BonusType.ONE_OFF);

      BonusType shouldException = (BonusType)getById(BonusType.MONTHLY,4);
  }
}

And the result:

C:\Documents and Settings\user\My Documents>java Utils
true
false
true
true
Exception in thread "main" java.lang.IllegalArgumentException: BonusType does not accept id 4
        at Utils.getById(Utils.java:6)
        at Utils.main(Utils.java:23)

C:\Documents and Settings\user\My Documents>
corsiKa
  • 81,495
  • 25
  • 153
  • 204
  • 1
    Just like with Turd Ferguson's answer, that's the unelegant solution I'd like to avoid / improve... – sleske Feb 16 '11 at 19:47
  • I usually create the reverse mappings in a static{ } block to not have to loop over values() every time I ask for a value by id. I also usually call the method valueOf(int) to make it appear somewhat like the valueOf(String) method already there for Strings (also part of the OP's question). Somewhat like item 33 in Effective Java: http://tinyurl.com/4ffvc38 – Fredrik Feb 16 '11 at 19:51
  • @Sleske Updated with a more refined solution. @Fredrik interesting, although I doubt the iteration is going to be a significant issue. – corsiKa Feb 16 '11 at 20:09
  • @glowcoder Well, not having to iterate more than once means that it doesn't matter if you do it a thousand times per second where it can be a very significant issue or just call it twice. – Fredrik Feb 16 '11 at 20:30
  • @Fredrik I'll admit there are times where it may be necessary to optimize. I'm also saying that until it's an identified performance issue, don't optimize for it. – corsiKa Feb 16 '11 at 21:01
  • @glowcoder I agree that premature optimizations should be avoided but that doesn't mean you shouldn't apply normal good engineering practices to what you do, right? It is not like such an optimization would obfuscate things or so and it is really more of a rather accepted best practice than a possibly not needed optimization. – Fredrik Feb 16 '11 at 23:06
  • Actually it would. It requires a static cache of the values. OP Specifically said he would like to avoid doing this, and doing so requires you to perform that operation for all new types. That's why I tried to (and feel I adequately did) devise a method that doesn't involve anything extra going into a class (at least that wouldn't get caught by a compiler.) – corsiKa Feb 16 '11 at 23:19
1

I'm not sure if it's the same in Java, but enum types in C are automatically mapped to integers as well so you can use either the type or integer to access it. Have you tried simply accessing it with integer yet?

Detra83
  • 271
  • 3
  • 3
  • 2
    Enums in Java don't behave that way. They're an explicit type. – Chris Thompson Feb 16 '11 at 19:39
  • Each enum object would have a internal number (namely the position in which it was declared), and it can be accessed by the `.ordinal()` method. (The other way, use `BonusType.values()[i]`.) But in the example cited above, the indexes here and the outside values don't coincide. – Paŭlo Ebermann Feb 16 '11 at 19:55
1

Really great question :-) I used solution similar to Mr.Ferguson`s sometime ago. Our decompiled enum looks like this:

final class BonusType extends Enum
{

    private BonusType(String s, int i, int id)
    {
        super(s, i);
        this.id = id;
    }

    public static BonusType[] values()
    {
        BonusType abonustype[];
        int i;
        BonusType abonustype1[];
        System.arraycopy(abonustype = ENUM$VALUES, 0, abonustype1 = new BonusType[i = abonustype.length], 0, i);
        return abonustype1;
    }

    public static BonusType valueOf(String s)
    {
        return (BonusType)Enum.valueOf(BonusType, s);
    }

    public static final BonusType MONTHLY;
    public static final BonusType YEARLY;
    public static final BonusType ONE_OFF;
    public final int id;
    private static final BonusType ENUM$VALUES[];

    static 
    {
        MONTHLY = new BonusType("MONTHLY", 0, 1);
        YEARLY = new BonusType("YEARLY", 1, 2);
        ONE_OFF = new BonusType("ONE_OFF", 2, 3);
        ENUM$VALUES = (new BonusType[] {
            MONTHLY, YEARLY, ONE_OFF
        });
    }
}

Seeing this is apparent why ordinal() is unstable. It is the i in super(s, i);. I'm also pessimistic that you can think of a more elegant solution than these you already enumerated. After all enums are classes as any final classes.

Lachezar Balev
  • 11,498
  • 9
  • 49
  • 72
1

For the sake of completeness, here is a generic approach to retrieve enum values by index from any enum type. My intention was to make the method look and feel like Enum.valueOf(Class, String). Fyi, i copied this method from here.

Index related issues (already discussed in depth here) still apply.

/**
 * Returns the {@link Enum} instance for a given ordinal.
 * This method is the index based alternative
 * to {@link Enum#valueOf(Class, String)}, which
 * requires the name of an instance.
 * 
 * @param <E> the enum type
 * @param type the enum class object
 * @param ordinal the index of the enum instance
 * @throws IndexOutOfBoundsException if ordinal < 0 || ordinal >= enums.length
 * @return the enum instance with the given ordinal
 */
public static <E extends Enum<E>> E valueOf(Class<E> type, int ordinal) {
    Preconditions.checkNotNull(type, "Type");
    final E[] enums = type.getEnumConstants();
    Preconditions.checkElementIndex(ordinal, enums.length, "ordinal");
    return enums[ordinal];
}
whiskeysierra
  • 5,030
  • 1
  • 29
  • 40
  • That's not really what I'm looking for, as this only retrieves enum values by their ordinal, not by an assigned integer id (see my question). Also, if I do want that, I can just use `MyEnumType.values()` - no need for a static helper method. – sleske Feb 17 '11 at 14:00
0
Int -->String :

public enum Country {

    US("US",0),
    UK("UK",2),
    DE("DE",1);


    private static Map<Integer, String> domainToCountryMapping; 
    private String country;
    private int domain;

    private Country(String country,int domain){
        this.country=country.toUpperCase();
        this.domain=domain;
    }

    public String getCountry(){
        return country;
    }


    public static String getCountry(String domain) {
        if (domainToCountryMapping == null) {
            initMapping();
        }

        if(domainToCountryMapping.get(domain)!=null){
            return domainToCountryMapping.get(domain);
        }else{
            return "US";
        }

    }

     private static void initMapping() {
         domainToCountryMapping = new HashMap<Integer, String>();
            for (Country s : values()) {
                domainToCountryMapping.put(s.domain, s.country);
            }
        }
Ran Adler
  • 3,587
  • 30
  • 27
0

I needed something different because I wanted to use a generic approach. I'm reading the enum to and from byte arrays. This is where I come up with:

public interface EnumConverter {
    public Number convert();
}



public class ByteArrayConverter {
@SuppressWarnings("unchecked")
public static Enum<?> convertToEnum(byte[] values, Class<?> fieldType, NumberSystem numberSystem) throws InvalidDataException {
    if (values == null || values.length == 0) {
        final String message = "The values parameter must contain the value";
        throw new IllegalArgumentException(message);
    }

    if (!dtoFieldType.isEnum()) {
        final String message = "dtoFieldType must be an Enum.";
        throw new IllegalArgumentException(message);
    }

    if (!EnumConverter.class.isAssignableFrom(fieldType)) {
        final String message = "fieldType must implement the EnumConverter interface.";
        throw new IllegalArgumentException(message);
    }

    Enum<?> result = null;
    Integer enumValue = (Integer) convertToType(values, Integer.class, numberSystem); // Our enum's use Integer or Byte for the value field.

    for (Object enumConstant : fieldType.getEnumConstants()) {
        Number ev = ((EnumConverter) enumConstant).convert();

        if (enumValue.equals(ev)) {
            result = (Enum<?>) enumConstant;
            break;
        }
    }

    if (result == null) {
        throw new EnumConstantNotPresentException((Class<? extends Enum>) fieldType, enumValue.toString());
    }

    return result;
}

public static byte[] convertEnumToBytes(Enum<?> value, int requiredLength, NumberSystem numberSystem) throws InvalidDataException {
    if (!(value instanceof EnumConverter)) {
        final String message = "dtoFieldType must implement the EnumConverter interface.";
        throw new IllegalArgumentException(message);
    }

    Number enumValue = ((EnumConverter) value).convert();
    byte[] result = convertToBytes(enumValue, requiredLength, numberSystem);
    return result;
}

public static Object convertToType(byte[] values, Class<?> type, NumberSystem numberSystem) throws InvalidDataException {
    // some logic to convert the byte array supplied by the values param to an Object.
}

public static byte[] convertToBytes(Object value, int requiredLength, NumberSystem numberSystem) throws InvalidDataException {
    // some logic to convert the Object supplied by the'value' param to a byte array.
}
}

Example of enum's:

public enum EnumIntegerMock implements EnumConverter {
    VALUE0(0), VALUE1(1), VALUE2(2);

    private final int value;

    private EnumIntegerMock(int value) {
        this.value = value;
    }

public Integer convert() {
    return value;
}

}

public enum EnumByteMock implements EnumConverter {
    VALUE0(0), VALUE1(1), VALUE2(2);

    private final byte value;

    private EnumByteMock(int value) {
        this.value = (byte) value;
    }

    public Byte convert() {
        return value;
    }
}
Patrick Koorevaar
  • 1,219
  • 15
  • 24
0

Just because the accepted answer is not self contained:

Support code:

public interface EnumWithCode<E extends Enum<E> & EnumWithCode<E>> {

    public Integer getCode();

    E fromCode(Integer code);
}


public class EnumWithCodeMap<V extends Enum<V> & EnumWithCode<V>> {

    private final HashMap<Integer, V> _map = new HashMap<Integer, V>();

    public EnumWithCodeMap(Class<V> valueType) {
        for( V v : valueType.getEnumConstants() )
            _map.put(v.getCode(), v);
    }

    public V get(Integer num) {
        return _map.get(num);
    }
}

Example of use:

public enum State implements EnumWithCode<State> {
    NOT_STARTED(0), STARTED(1), ENDED(2);

    private static final EnumWithCodeMap<State> map = new EnumWithCodeMap<State>(
            State.class);

    private final int code;

    private State(int code) {
        this.code = code;
    }

    @Override
    public Integer getCode() {
        return code;
    }

    @Override
    public State fromCode(Integer code) {
        return map.get(code);
    }

}
leonbloy
  • 73,180
  • 20
  • 142
  • 190
0

given:

public enum BonusType { MONTHLY(0), YEARLY(1), ONE_OFF(2) }

BonusType bonus = YEARLY;

System.out.println(bonus.Ordinal() + ":" + bonus)

Output: 1:YEARLY

David Urry
  • 807
  • 1
  • 8
  • 15
0

If you have a class Car

public class Car {
    private Color externalColor;
}

And the property Color is a class

@Data
public class Color {
    private Integer id;
    private String name;
}

And you want to convert Color to an Enum

public class CarDTO {
    private ColorEnum externalColor;
}

Simply add a method in Color class to convert Color in ColorEnum

@Data
public class Color {
    private Integer id;
    private String name;

    public ColorEnum getEnum(){
        ColorEnum.getById(id);
    }
}

and inside ColorEnum implements the method getById()

public enum ColorEnum {
...
    public static ColorEnum getById(int id) {
        for(ColorEnum e : values()) {
            if(e.id==id) 
                return e;
        }
    }
}

Now you can use a classMap

private MapperFactory factory = new DefaultMapperFactory.Builder().build();
...
factory.classMap(Car.class, CarDTO.class)
    .fieldAToB("externalColor.enum","externalColor")
    .byDefault()
    .register();
...
CarDTO dto = mapper.map(car, CarDTO.class);
danilo
  • 7,680
  • 7
  • 43
  • 46