Получить экземпляр Enum из класса <? extends Enum> использует значение String?
Мне сложно помещать точный вопрос в слова, поэтому я просто приведу пример.
У меня есть два типа 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
.)
Спасибо!
Ответы
Ответ 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");
Ответ 2
Итак, вот код, использующий валидацию 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
Мы хотим получить объект Method
, который отражает метод valueOf
переданного Class
, который принимает параметр String
; то invoke
он без объекта (поскольку он статический) и предоставленный параметр String:
type.getDeclaredMethod("valueOf", String.class).invoke(null, value);
Вам нужно будет ловить лодку из разных типов исключений.
Ответ 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.RED
, так и NuclearThreatWarningLevel.RED
, вы можете, по крайней мере, оказаться в неправильном классе. В худшем случае вы можете оказаться в своем подземном бункере в течение 6 месяцев, ожидая, когда воздух очистится, когда все, что вам нужно, - это автомобиль, окрашенный в красный цвет:)
Было бы лучше перепроектировать эту область вашего кода, если это возможно, поэтому вам не нужно динамически преобразовывать строку в экземпляр одного из нескольких классов. Если вы добавите свой ответ, чтобы включить проблему, которую вы пытаетесь решить, возможно, у сообщества SO будет несколько идей.