Удобно отображать между enum и int / String - PullRequest
105 голосов
/ 16 февраля 2011

При работе с переменными / параметрами, которые могут принимать только конечное число значений, я стараюсь всегда использовать Java enum, как в

public enum BonusType {
  MONTHLY, YEARLY, ONE_OFF
}

Пока я остаюсь внутри своего кода, это прекрасно работает. Однако мне часто приходится взаимодействовать с другим кодом, который использует простые значения int (или String) для той же цели, или мне нужно читать / записывать из / в базу данных, где данные хранятся в виде числа или строки .

В этом случае я хотел бы иметь удобный способ связать каждое значение перечисления с целым числом, чтобы я мог преобразовать оба способа (другими словами, мне нужен «обратимый перечислитель»).

Переход от enum к int очень прост:

public enum BonusType {
  public final int id;

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

Тогда я могу получить доступ к значению int как BonusType x = MONTHLY; int id = x.id;.

Однако я не вижу хорошего пути для обратного, то есть перехода от int к enum. В идеале что-то вроде

BonusType bt = BonusType.getById(2); 

Единственные решения, которые я мог придумать, это:

  • Поместите метод поиска в enum, который использует BonusType.values() для заполнения карты "int -> enum", затем кэширует его и использует для поиска. Работало бы, но мне пришлось бы копировать этот метод одинаково в каждое перечисление, которое я использую: - (.
  • Поместите метод поиска в статический служебный класс. Тогда мне нужен был бы только один метод «поиска», но мне пришлось бы поиграть с отражением, чтобы заставить его работать для произвольного перечисления.

Оба метода кажутся ужасно неловкими для такой простой (?) Проблемы.

Любые другие идеи / идеи?

Ответы [ 17 ]

321 голосов
/ 16 февраля 2011

enum → int

yourEnum.ordinal()

int → enum

EnumType.values()[someInt]

String → enum

EnumType.valueOf(yourString)

enum → String

yourEnum.name()

Примечание:
Как вы правильно заметили, ordinal() может быть «нестабильным» от версии к версии. Именно по этой причине я всегда храню константы в виде строк в своих базах данных. (На самом деле, при использовании MySql я сохраняю их как MySql перечисления !)

36 голосов
/ 16 февраля 2011

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

Решение начинается аналогично вашему, со значением int как частью определения enum. Затем он продолжает создание универсальной поисковой утилиты:

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

Это хорошее решение, не требующее "раздумий с отражением", поскольку оно основано на том факте, что все типы enum неявно наследуют интерфейс Enum.

27 голосов
/ 17 сентября 2011

Я нашел это в Интернете, это было очень полезно и просто для реализации. Это решение НЕ было сделано мной

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

}

6 голосов
/ 02 декабря 2015

Кажется, что ответ (ы) на этот вопрос устарели с выпуском Java 8.

  1. Не используйте порядковый номер, так как порядковый номер нестабилен, если сохраняется вне JVM, такой как база данных.
  2. Создать статическую карту с ключевыми значениями относительно просто.

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);
  }
}
4 голосов
/ 05 сентября 2014

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

Чтобы избавить меня от написания множества шаблонного кода или дублирующего кода для каждого Enum, вместо этого я использовал ValuedEnum Apache Commons Lang.1005 * Определение :

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

Использование:

int -> ValuedEnum:

NRPEPacketType packetType = 
 (NRPEPacketType) EnumUtils.getEnum(NRPEPacketType.class, 1);
3 голосов
/ 16 февраля 2011

Возможно, вы могли бы использовать что-то вроде

interface EnumWithId {
    public int getId();

}


enum Foo implements EnumWithId {

   ...
}

Это уменьшило бы необходимость отражения в вашем служебном классе.

3 голосов
/ 18 июня 2015

В этом коде для постоянного и интенсивного поиска есть память или процесс для использования, и я выбираю память с массивом преобразователей в качестве индекса.Я надеюсь, что это полезно

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]];
}
}
2 голосов
/ 25 сентября 2013

Очень чистый пример использования обратного Enum

Шаг 1 Определение interface EnumConverter

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

Шаг2

Создать имя класса 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);
    }
}

Шаг 3

Перейти к вам Enum класс и implement itс EnumConverter<ContentType> и, конечно, переопределить методы интерфейса.Вам также нужно инициализировать статический 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);
    }
}

Шаг 4

Теперь создайте файл класса Communication и вызовите его новый метод для преобразования Enum до String и String до Enum.Я только что изложил основной метод для объяснения.

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

Нажмите для полного объяснения

2 голосов
/ 16 февраля 2011

Используйте интерфейс, чтобы показать, кто в доме хозяин.

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

И результат:

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>
2 голосов
/ 10 мая 2013

И .ordinal(), и values()[i] нестабильны, поскольку они зависят от порядка перечислений.Таким образом, если вы измените порядок перечислений или добавите / удалите некоторые, ваша программа сломается.

Вот простой, но эффективный метод для отображения между enum и 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");
    }
}

Применение его для строк не должно быть сложным.

...