web-dev-qa-db-fra.com

Obtenir une instance d'énumération à partir de la classe <? étend Enum> en utilisant la valeur de chaîne?

J'ai du mal à formuler la question exacte avec des mots, je vais donc donner un exemple.

J'ai deux types de Enum:

enum Shape {
    CAT, DOG;
}

enum Color {
    BLUE, RED;
}

J'ai une méthode:

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

Je voudrais utiliser la méthode comme:

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

J'ai eu du mal à déterminer exactement comment implémenter getInstance(). Une fois que vous connaissez la classe Enum exacte que vous souhaitez instancier, rien de plus simple:

Color.valueOf("RED");

Mais comment réaliser cette ligne ci-dessus avec un Class inconnu? (Il est cependant connu que le someEnumClass est une sous-classe de Enum.)

Merci!

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

Et la méthode doit être utilisée comme:

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

Là encore, vous pouvez toujours utiliser

final Shape shape = Shape.valueOf("CAT");

qui est un raccourci pour

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

Voici donc le code qui utilise la validation Spring et qui fonctionne très bien pour moi. Code complet donné ci-dessous.

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

}

Implémentation de la classe ci-dessus:

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

  }

}

UTILISATION DE L'ANNOTATION CI-DESSUS IS TRÈS SIMPLE

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

Nous voulons obtenir l'objet Method qui reflète la méthode valueOf du Class transmis, qui accepte un paramètre String; puis invoke sans objet (car il est statique) et le paramètre String fourni:

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

Vous devrez attraper une cargaison de différents types d'exceptions.

1
Karl Knechtel

Puisque vous avez une idée de la classe que vous recherchez, vous pouvez simplement demander à l'énumération si elle sait ce qui vous intéresse:

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

Cependant, le fait de brancher des chaînes dans plusieurs énumérations à la recherche d'un ajustement compromet la sécurité du type que vous obtenez avec les énumérations. Si vous mappez "rouge" aux deux MyColor.RED et NuclearThreatWarningLevel.RED alors vous pourriez au moins vous retrouver avec la mauvaise classe. Au pire, vous pourriez vous retrouver dans votre bunker souterrain pendant 6 mois en attendant que l'air se dégage, alors que tout ce que vous vouliez était une voiture peinte en rouge :)

Il serait préférable de reconcevoir cette zone de votre code si possible afin de ne pas avoir à convertir dynamiquement une chaîne en une instance de plusieurs classes. Si vous développez votre réponse pour inclure le problème que vous essayez de résoudre, peut-être que la communauté SO aura quelques idées.

0
Paul