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!
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");
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;
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.
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.