使用字符串值从Class<? extends Enum>获取枚举实例?

31

我很难用言语准确表达问题,所以我将只给一个示例。

我有两个Enum类型:

enum Shape {
    CAT, DOG;
}

enum Color {
    BLUE, RED;
}

我有一个方法:

public Object getInstance(String value, Class<?> type);

我想使用这种方法:

// someValue is probably "RED", and someEnumClass is probably Color.class
Color c = getInstance(someValue, someEnumClass);

我一直很难确定如何实现getInstance()方法。一旦你知道要实例化的确切Enum类,这就很容易了:

Color.valueOf("RED");

但是如何在未知的 Class 中实现上述行为呢?(不过,已知 someEnumClassEnum 的子类。)

谢谢!

4个回答

57
 public static <T extends Enum<T>> T getInstance(final String value, final Class<T> enumClass) {
     return Enum.valueOf(enumClass, value);
 }

这个方法的使用方式如下:

final Shape shape = getInstance("CAT", Shape.class);

再说一遍,您总是可以使用。
final Shape shape = Shape.valueOf("CAT");

这是一个代表

的快捷方式。

Enum.valueOf(Shape.class, "CAT");

+1 枚举类型是为了让你不必自己通过反射机制去处理。经过良好的解析和识别。 - Karl Knechtel
为什么?因为他要求这样做。我试图指出他问题的解决方案。 - chahuistle
非常好,感谢您提供的解决方案和详细的说明。Enum.valueOf() 方法完美地解决了问题! - Craig Otis
2
还有@Paul,我正在构建一些具有枚举类型实例的类,但是枚举值以字符串形式提供。 (例如存储在数据库中。)我使用反射确定枚举的实际类型,然后使用Enum.valueOf()与已知类和字符串值设置字段。使用反射和Enum.valueOf()使得该方法可以在不必知道实际存在的枚举类的情况下工作。 - Craig Otis
1
这是我问题的更简洁版本:https://dev59.com/Pm435IYBdhLWcg3wtSYV - Craig Otis
不能用于具有重写方法的枚举。 - Learner

4
我们想获取反映传入的Class的valueOf方法的Method对象,该方法接受一个String参数; 然后使用没有对象(因为它是静态的)和提供的String参数来调用它:
type.getDeclaredMethod("valueOf", String.class).invoke(null, value);

您需要捕获许多不同类型的异常。


3

以下是使用Spring验证的代码,对我来说非常有效。完整的代码如下。

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import javax.validation.Constraint;
import javax.validation.Payload;
import javax.validation.ReportAsSingleViolation;
import javax.validation.constraints.NotNull;

@Documented
@Constraint(validatedBy = EnumValidatorImpl.class)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@NotNull(message = "Value cannot be null")
@ReportAsSingleViolation
public @interface EnumValidator {

  Class<? extends Enum<?>> enumClazz();

  String message() default "Value is not valid";

  Class<?>[] groups() default {};

  Class<? extends Payload>[] payload() default {};

}

以上类的实现:

import java.util.ArrayList;
import java.util.List;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

public class EnumValidatorImpl implements ConstraintValidator<EnumValidator, String> {

  List<String> valueList = null;

  @Override
  public boolean isValid(String value, ConstraintValidatorContext context) {
    if(!valueList.contains(value.toUpperCase())) {
      return false;
    }
    return true;
  }

  @Override
  public void initialize(EnumValidator constraintAnnotation) {
    valueList = new ArrayList<String>();
    Class<? extends Enum<?>> enumClass = constraintAnnotation.enumClazz();

    @SuppressWarnings("rawtypes")
    Enum[] enumValArr = enumClass.getEnumConstants();

    for(@SuppressWarnings("rawtypes")
    Enum enumVal : enumValArr) {
      valueList.add(enumVal.toString());
    }

  }

}

以上标注的使用非常简单。

 @JsonProperty("lead_id")
  @EnumValidator( enumClazz=DefaultEnum.class,message="This error is coming from the enum class", groups = {Group1.class })
  private String leadId;

3
欢迎来到StackOverflow!OP正在询问如何通过枚举类和字符串获取枚举值。 - kukido

0

既然你已经知道你要找的类,你可以直接问枚举它是否知道你感兴趣的内容:

public enum MyColor
{
  RED  ("red", Color.RED),
  BLUE ("blue", Color.BLUE),
  TAUPE ("brownish", new COLOR(80,64,77));

  private final String _name;
  private final Color _color;

  MyColor(String name, Color color)
  {
    _name = name;
    _color = color;
  }

  public static Color parseColor(String colorName)
  {
    for (MyColor mc : MyColor.values())
    {
      if (mc._name.equalsIgnoreCase(colorName))
        return mc._color;
    }
    return null;
  }
}

然而,将字符串插入多个枚举中寻找匹配会损害枚举所提供的类型安全性。如果你将“red”映射到MyColor.REDNuclearThreatWarningLevel.RED,那么你可能最起码会得到错误的类。在最坏的情况下,你可能会在地下掩体里等待6个月,等待空气变清新,而你真正想要的只是一辆红色的汽车 :)

如果可能的话,最好重新设计代码中的这个区域,这样你就不必动态地将字符串转换为几个类的实例。如果你扩展你的答案以包括你试图解决的问题,也许SO社区会有一些想法。


看到 chahuistle 的回答中提到了我的 @ 符号。实际上我并不知道模型中存在哪些枚举类。未来可能会添加/删除一些枚举类。我使用反射在运行时确定字段的类型。此外,这些值是从数据库加载的字符串,因此我需要灵活处理可以处理的类/值。 - Craig Otis
@craig,类名以字符串形式存储在数据库中吗?您如何知道要实例化哪个类? - Paul
类名未存储在数据库中。我有几个类“A”,“B”和“C”,它们都包含指向某种枚举类型的ivar。这些枚举在类中是静态类型的,例如,“A”具有颜色枚举,而“B”具有形状枚举,但是我正在使用单个方法为这些枚举字段分配值,该方法使用反射来确定(基于A,B或C的某个实例以及字符串值),需要使用哪种枚举类型。 - Craig Otis
这样,当我们添加另一个使用新的枚举类型(“Food”)的类“D”时,即使在编写时该枚举类型不存在,从数据库加载值的方法仍将正常工作,无需进行任何调整或维护。 - Craig Otis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接