当处理只能获取有限数量值的变量/参数时,我尝试始终使用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);
我能提出的唯一解决方案是:
BonusType.values()
填充地图“int - > enum”,然后缓存并将其用于查找。会工作,但我必须将这个方法完全复制到我使用的每个枚举中: - (。对于这样一个简单的(?)问题,这两种方法看起来都非常尴尬。
还有其他想法/见解吗?
答案 0 :(得分:321)
yourEnum.ordinal()
EnumType.values()[someInt]
EnumType.valueOf(yourString)
yourEnum.name()
附注:
正如您正确指出的那样,ordinal()
从版本到版本可能会“不稳定”。这就是为什么我总是将常量存储为数据库中的字符串的确切原因。 (实际上,当使用MySql时,我将它们存储为MySql enums!)
答案 1 :(得分:36)
http://www.javaspecialists.co.za/archive/Issue113.html
解决方案与您的解决方案类似,使用int值作为枚举定义的一部分。然后,他继续创建基于泛型的查找实用程序:
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接口的事实。
答案 2 :(得分:27)
我在网上发现了这个,它非常有用且易于实现。 这个解决方案不是由我做的
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);
}
}
答案 3 :(得分:6)
似乎这个问题的答案已经过时了Java 8的发布。
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 :(得分:4)
为了节省我为每个枚举编写大量样板代码或复制代码,我使用了Apache Commons Lang ValuedEnum
。
<强>定义强>:
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 - &gt; ValuedEnum:强>
NRPEPacketType packetType =
(NRPEPacketType) EnumUtils.getEnum(NRPEPacketType.class, 1);
答案 5 :(得分:3)
您可以使用类似
的内容interface EnumWithId {
public int getId();
}
enum Foo implements EnumWithId {
...
}
这样可以减少实用程序类中反射的需要。
答案 6 :(得分:3)
在此代码中,对于永久性和强烈搜索,请使用内存或进程,并选择内存,将转换器数组作为索引。 我希望它有用
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]];
}
}
答案 7 :(得分:2)
使用界面向其展示谁是老板。
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>
答案 8 :(得分:2)
反向枚举的非常简洁的用法示例
第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
课程和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)
}
}
答案 9 :(得分:2)
.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");
}
}
将它应用于字符串应该不难。
答案 10 :(得分:1)
为了完整起见,这里是一种通过索引从任何枚举类型中检索枚举值的通用方法。我的目的是使方法看起来像Enum.valueOf(Class, String)。 Fyi,我从here复制了这个方法。
指数相关问题(此处已深入讨论)仍然适用。
/**
* 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];
}
答案 11 :(得分:1)
非常好的问题:-)我之前使用的解决方案类似于Mr.Ferguson。我们的反编译枚举看起来像这样:
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);
。我也很悲观,你可以想到一个比你已经列举过的更优雅的解决方案。所有的词汇都是最后一堂课。
答案 12 :(得分:1)
我不确定它在Java中是否相同,但C中的枚举类型也会自动映射到整数,因此您可以使用类型或整数来访问它。您是否尝试过使用整数访问它?
答案 13 :(得分: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);
}
}
答案 14 :(得分:0)
我需要不同的东西,因为我想使用通用方法。我正在从字节数组中读取枚举。这是我想出的地方:
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;
}
}
答案 15 :(得分:0)
给定:
公共枚举BonusType { 每月(0),每年(1),一次关闭(2) }
BonusType奖励=每年;
System.out.println(bonus.Ordinal()+“:” +红利)
输出: 1:每年
答案 16 :(得分:0)
如果您有课程汽车
public class Car {
private Color externalColor;
}
属性颜色是一个类
@Data
public class Color {
private Integer id;
private String name;
}
您想将颜色转换为枚举
public class CarDTO {
private ColorEnum externalColor;
}
只需在 Color 类中添加一种方法即可转换 ColorEnum
中的 Color@Data
public class Color {
private Integer id;
private String name;
public ColorEnum getEnum(){
ColorEnum.getById(id);
}
}
在 ColorEnum 内部实现了getById()方法
public enum ColorEnum {
...
public static ColorEnum getById(int id) {
for(ColorEnum e : values()) {
if(e.id==id)
return e;
}
}
}
现在您可以使用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);