Удобно отображать между 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 ]

1 голос
/ 17 февраля 2011

Для полноты картины приведем общий подход для получения значений перечисления по индексу из любого типа перечисления.Мое намерение состояло в том, чтобы сделать метод похожим на Enum.valueOf (Class, String) .К вашему сведению, я скопировал этот метод из здесь .

Вопросы, связанные с индексами (уже подробно обсуждались здесь), все еще применяются.

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

Действительно отличный вопрос :-) Я использовал решение, аналогичное тому, что было у Mr.Ferguson's некоторое время назад.Наше декомпилированное перечисление выглядит так:

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

Видно, что очевидно, почему ordinal() неустойчиво.Это i в super(s, i);.Я также пессимистичен, что вы можете придумать более элегантное решение, чем те, которые вы уже перечислили.Ведь перечисления - это классы, как и все финальные классы.

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

Я не уверен, что это то же самое в Java, но типы enum в C автоматически сопоставляются с целыми числами, поэтому вы можете использовать либо тип, либо целое число для доступа к нему.Вы пробовали просто получить к нему доступ с помощью целого числа?

0 голосов
/ 22 февраля 2019

Дано:

public enum BonusType { ЕЖЕМЕСЯЧНО (0), ГОД (1), ONE_OFF (2) }

BonusType bonus = YEARLY; ​​

System.out.println (bonus.Ordinal () + ":" + бонус)

Выход: 1: ЕЖЕГОДНО

0 голосов
/ 19 июня 2013
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);
            }
        }
0 голосов
/ 08 мая 2017

Только потому, что принятый ответ не является самодостаточным:

Код поддержки:

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

Пример использования:

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

}
0 голосов
/ 31 марта 2015

Мне нужно было что-то другое, потому что я хотел использовать общий подход.Я читаю перечисление в и из байтовых массивов.Вот где я придумаю:

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

Пример перечисления:

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