I'm finding it difficult to put the exact question into words, so I'll just give an example.
I have two Enum
types:
enum Shape开发者_运维知识库 {
CAT, DOG;
}
enum Color {
BLUE, RED;
}
I have a method:
public Object getInstance(String value, Class<?> type);
I would like to use the method like:
// someValue is probably "RED", and someEnumClass is probably Color.class
Color c = getInstance(someValue, someEnumClass);
I've been having trouble determining exactly how to implement getInstance()
. Once you know the exact Enum
class that you want to instantiate, it's easy:
Color.valueOf("RED");
But how can this above line be accomplished with an unknown Class
? (It is, however, known that the someEnumClass
is a subclass of Enum
.)
Thanks!
public static <T extends Enum<T>> T getInstance(final String value, final Class<T> enumClass) {
return Enum.valueOf(enumClass, value);
}
And the method is to be used as:
final Shape shape = getInstance("CAT", Shape.class);
Then again, you can always use
final Shape shape = Shape.valueOf("CAT");
which is a shortcut for
Enum.valueOf(Shape.class, "CAT");
We want to get the Method
object which reflects the valueOf
method of the passed-in Class
, which accepts a String
parameter; then invoke
it with no object (since it's static) and the supplied String parameter:
type.getDeclaredMethod("valueOf", String.class).invoke(null, value);
You will need to catch a boatload of different types of exceptions.
So here is the code being using Spring validation and works great for me. Full code given below.
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 {};
}
Implementation of the above class:
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());
}
}
}
USAGE OF THE ABOVE ANNOTATION IS VERY SIMPLE
@JsonProperty("lead_id")
@EnumValidator( enumClazz=DefaultEnum.class,message="This error is coming from the enum class", groups = {Group1.class })
private String leadId;
Since you have an idea of what class you're looking for you can just ask the enum if it knows what you're interested in:
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;
}
}
However, plugging strings into multiple enums looking for a fit compromises the type safety you get with enums. If you map "red" to both MyColor.RED
and NuclearThreatWarningLevel.RED
then you may, at the very least, end up with the wrong class. At the worst you could end up in your underground bunker for 6 months waiting for the air to clear, when all you wanted was a car painted red :)
It would be better to redesign this area of your code if possible so you don't have to convert a string to an instance of one of several classes dynamically. If you expand your answer to include the problem you're trying to solve perhaps the SO community will have some ideas.
精彩评论