Преобразовать целочисленное значение в соответствующий Java Enum
У меня есть перечисление как это:
public enum PcapLinkType {
DLT_NULL(0)
DLT_EN10MB(1)
DLT_EN3MB(2),
DLT_AX25(3),
/*snip, 200 more enums, not always consecutive.*/
DLT_UNKNOWN(-1);
private final int value;
PcapLinkType(int value) {
this.value= value;
}
}
Теперь я получаю int из внешнего ввода и хочу соответствующий вход - выбрасывать исключение, если значение не существует, это нормально, но желательно, чтобы оно было DLT_UNKNOWN
в таком случае.
int val = in.readInt();
PcapLinkType type = ???; /*convert val to a PcapLinkType */
12 ответов
Вам нужно будет сделать это вручную, добавив статическую карту в класс, который отображает целые числа в перечисления, такие как
private static final Map<Integer, PcapLinkType> intToTypeMap = new HashMap<Integer, PcapLinkType>();
static {
for (PcapLinkType type : PcapLinkType.values()) {
intToTypeMap.put(type.value, type);
}
}
public static PcapLinkType fromInt(int i) {
PcapLinkType type = intToTypeMap.get(Integer.valueOf(i));
if (type == null)
return PcapLinkType.DLT_UNKNOWN;
return type;
}
Там есть статический метод values()
что задокументировано, но не там, где вы этого ожидаете: http://docs.oracle.com/javase/tutorial/java/javaOO/enum.html
enum MyEnum {
FIRST, SECOND, THIRD;
private static MyEnum[] allValues = values();
public static MyEnum fromOrdinal(int n) {return allValues[n];}
}
В принципе, вы можете использовать только values()[i]
Но ходят слухи, что values()
создаст копию массива каждый раз, когда он вызывается.
Вам нужно будет создать новый статический метод, в котором вы итерируете PcapLinkType.values () и сравниваете:
public static PcapLinkType forCode(int code) {
for (PcapLinkType typе : PcapLinkType.values()) {
if (type.getValue() == code) {
return type;
}
}
return null;
}
Это было бы хорошо, если это называется редко. Если он вызывается часто, то посмотрите на Map
оптимизация, предложенная другими.
Если у вас есть перечисление, как это
public enum PcapLinkType {
DLT_NULL(0)
DLT_EN10MB(1)
DLT_EN3MB(2),
DLT_AX25(3),
DLT_UNKNOWN(-1);
private final int value;
PcapLinkType(int value) {
this.value= value;
}
}
тогда вы можете использовать его как
PcapLinkType type = PcapLinkType.values()[1]; /*convert val to a PcapLinkType */
Вы можете сделать что-то вроде этого, чтобы автоматически зарегистрировать их все в коллекцию, чтобы затем легко конвертировать целые числа в соответствующее перечисление. (Кстати, добавление их на карту в конструкторе enum недопустимо. Приятно изучать новые вещи даже после многих лет использования Java.:)
public enum PcapLinkType {
DLT_NULL(0),
DLT_EN10MB(1),
DLT_EN3MB(2),
DLT_AX25(3),
/*snip, 200 more enums, not always consecutive.*/
DLT_UNKNOWN(-1);
private static final Map<Integer, PcapLinkType> typesByValue = new HashMap<Integer, PcapLinkType>();
static {
for (PcapLinkType type : PcapLinkType.values()) {
typesByValue.put(type.value, type);
}
}
private final int value;
private PcapLinkType(int value) {
this.value = value;
}
public static PcapLinkType forValue(int value) {
return typesByValue.get(value);
}
}
Я знаю, что этому вопросу уже несколько лет, но, тем временем, Java 8 принесла нам Optional
Я думал, что предложу решение, используя его (и Stream
а также Collectors
):
public enum PcapLinkType {
DLT_NULL(0),
DLT_EN3MB(2),
DLT_AX25(3),
/*snip, 200 more enums, not always consecutive.*/
// DLT_UNKNOWN(-1); // <--- NO LONGER NEEDED
private final int value;
private PcapLinkType(int value) { this.value = value; }
private static final Map<Integer, PcapLinkType> map;
static {
map = Arrays.stream(values())
.collect(Collectors.toMap(e -> e.value, e -> e));
}
public static Optional<PcapLinkType> fromInt(int value) {
return Optional.ofNullable(map.get(value));
}
}
Optional
как null
: это представляет случай, когда нет (действительного) значения. Но это более безопасная альтернатива null
или значение по умолчанию, такое как DLT_UNKNOWN
потому что вы могли бы забыть проверить null
или же DLT_UNKNOWN
случаев. Они оба действительны PcapLinkType
ценности! Напротив, вы не можете назначить Optional<PcapLinkType>
значение переменной типа PcapLinkType
, Optional
сначала проверяет правильность значения
Конечно, если вы хотите сохранить DLT_UNKNOWN
для обратной совместимости или по любой другой причине, вы все еще можете использовать Optional
даже в этом случае, используя orElse()
чтобы указать его как значение по умолчанию:
public enum PcapLinkType {
DLT_NULL(0),
DLT_EN3MB(2),
DLT_AX25(3),
/*snip, 200 more enums, not always consecutive.*/
DLT_UNKNOWN(-1);
private final int value;
private PcapLinkType(int value) { this.value = value; }
private static final Map<Integer, PcapLinkType> map;
static {
map = Arrays.stream(values())
.collect(Collectors.toMap(e -> e.value, e -> e));
}
public static PcapLinkType fromInt(int value) {
return Optional.ofNullable(map.get(value)).orElse(DLT_UNKNOWN);
}
}
Как говорит @MeBigFatGuy, только вы можете сделать static {...}
блок использовать петлю над values()
коллекция:
static {
for (PcapLinkType type : PcapLinkType.values()) {
intToTypeMap.put(type.getValue(), type);
}
}
Вы можете добавить статический метод в свой enum, который принимает int
в качестве параметра и возвращает PcapLinkType
,
public static PcapLinkType of(int linkType) {
switch (linkType) {
case -1: return DLT_UNKNOWN
case 0: return DLT_NULL;
//ETC....
default: return null;
}
}
Это то, что я использую:
public enum Quality {ENOUGH,BETTER,BEST;
private static final int amount = EnumSet.allOf(Quality.class).size();
private static Quality[] val = new Quality[amount];
static{ for(Quality q:EnumSet.allOf(Quality.class)){ val[q.ordinal()]=q; } }
public static Quality fromInt(int i) { return val[i]; }
public Quality next() { return fromInt((ordinal()+1)%amount); }
}
Возможно, это не лучшее решение, но оно работает для меня:
public enum Type {
WATER, FIRE, GRASS;
public static Type getType(int value){
if(value==WATER.ordinal()){
return WATER;
}else if(value==FIRE.ordinal()){
return FIRE;
}else if(value==GRASS.ordinal()){
return GRASS;
}else {
return null;
}
}
}
static final PcapLinkType[] values = { DLT_NULL, DLT_EN10MB, DLT_EN3MB, null ...}
...
public static PcapLinkType getPcapLinkTypeForInt(int num){
try{
return values[int];
}catch(ArrayIndexOutOfBoundsException e){
return DLT_UKNOWN;
}
}
Не существует способа элегантной обработки перечислимых типов на основе целых чисел. Вы можете подумать об использовании перечисления на основе строк вместо вашего решения. Не всегда предпочтительный способ, но он все еще существует.
public enum Port {
/**
* The default port for the push server.
*/
DEFAULT("443"),
/**
* The alternative port that can be used to bypass firewall checks
* made to the default <i>HTTPS</i> port.
*/
ALTERNATIVE("2197");
private final String portString;
Port(final String portString) {
this.portString = portString;
}
/**
* Returns the port for given {@link Port} enumeration value.
* @return The port of the push server host.
*/
public Integer toInteger() {
return Integer.parseInt(portString);
}
}