Получить экземпляр Enum из класса <? расширяет Enum> используя строковое значение? - PullRequest
24 голосов
/ 21 июля 2011

Мне трудно сформулировать точный вопрос словами, поэтому я просто приведу пример.

У меня есть два 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? (Однако известно, что someEnumClass является подклассом Enum.)

Спасибо!

Ответы [ 4 ]

43 голосов
/ 21 июля 2011
 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");
2 голосов
/ 12 января 2014

Итак, этот код использует 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;
1 голос
/ 21 июля 2011

Мы хотим получить объект Method, который отражает метод valueOf переданного Class, который принимает параметр String; затем invoke без объекта (поскольку он статический) и с предоставленным параметром String:

type.getDeclaredMethod("valueOf", String.class).invoke(null, value);

Вам нужно будет поймать лодку с различными типами исключений.

0 голосов
/ 21 июля 2011

Поскольку у вас есть представление о том, какой класс вы ищете, вы можете просто спросить enum, знает ли он, что вас интересует:

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.RED и NuclearThreatWarningLevel.RED, то вы, по крайней мере, можете получить неправильный класс. В худшем случае вы можете оказаться в подземном бункере в течение 6 месяцев, ожидая, пока воздух не очистится, а все, что вам нужно, - это машина, окрашенная в красный цвет:)

Было бы лучше изменить эту область вашего кода, если это возможно, чтобы вам не приходилось динамически преобразовывать строку в экземпляр одного из нескольких классов. Если вы расширите свой ответ, включив в него проблему, которую вы пытаетесь решить, возможно, у сообщества SO появятся некоторые идеи.

...