【问题标题】:Get Enum Instance from Class<? extends Enum> using String value?从 Class<? 中获取枚举实例使用字符串值扩展枚举>?
【发布时间】:2011-10-09 18:43:43
【问题描述】:

我发现很难用语言表达确切的问题,所以我只举一个例子。

我有两种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 的子类。)

谢谢!

【问题讨论】:

    标签: java class reflection enums instantiation


    【解决方案1】:
     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 枚举是为您构建的,您无需自己通过反射机制。很好的解析和发现。
    • 为什么?因为他要求。我试图指出他的问题的解决方案。
    • 非常好,感谢您的解决方案和详细解释。 Enum.valueOf() 方法完美运行!
    • 另外@Paul,我正在构建将枚举作为一些 ivars 的类的实例,但枚举的值作为字符串提供。 (例如,存储在数据库中。)我使用反射来确定 Enum 的实际类型,然后将 Enum.valueOf() 与已知类和字符串值一起使用来设置字段。使用反射和 Enum.valueOf() 允许此方法工作,而无需知道实际存在哪些 Enum 类。
    • 这是我的问题的更简洁的版本:stackoverflow.com/questions/5262096/…
    【解决方案2】:

    我们要获取Method对象,它反映了传入ClassvalueOf方法,该方法接受String参数;然后 invoke 它没有对象(因为它是静态的)和提供的字符串参数:

    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;
      

      【讨论】:

      • 欢迎来到 StackOverflow! OP 要求通过 Enum 类和 String 获取 Enum 值。
      【解决方案4】:

      既然你知道你要找什么类,你可以问枚举它是否知道你感兴趣的东西:

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

      但是,将字符串插入多个枚举以寻找合适的方式会损害使用枚举获得的类型安全性。如果您将“红色”映射到MyColor.REDNuclearThreatWarningLevel.RED,那么您至少可能会得到错误的类。在最坏的情况下,你可能会在地下掩体中待上 6 个月等待空气变清,而此时你想要的只是一辆漆成红色的汽车 :)

      如果可能的话,最好重新设计代码的这个区域,这样您就不必动态地将字符串转换为多个类之一的实例。如果您扩展您的答案以包含您要解决的问题,那么 SO 社区可能会有一些想法。

      【讨论】:

      • 在 chahuistle 的回答中查看我的 @。我实际上并不知道模型中存在哪些 Enum 类。将来,可能会添加/删除一些。我在字段上使用反射来确定运行时的类型。此外,这些值作为来自数据库加载的字符串提供,因此我需要灵活处理我可以处理的类/值。
      • @craig,类名是否作为字符串存储在数据库中?你怎么知道要实例化哪个类?
      • 类名未存储在数据库中。我有几个类“A”、“B”和“C”,每个类都包含某种类型的枚举的 ivar。枚举是静态类型在类中的,例如。 “A”有一个颜色枚举,而“B”有一个形状枚举,但我使用一种使用反射来确定的单一方法分配这些枚举字段(基于 A、B 或 C 的某些实例和一个字符串value) 需要使用哪个 Enum 类型。
      • 这样,当我们添加另一个类“D”时,该类“D”使用在撰写本文时不存在的新枚举类型(“Food”),该方法从数据库中加载值无需任何调整或维护,仍然可以正常工作。
      猜你喜欢
      • 2010-12-10
      • 1970-01-01
      • 1970-01-01
      • 2010-11-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多