2011-07-20 5 views
20

Me resulta difícil poner la pregunta exacta en palabras, así que solo daré un ejemplo.Obtener instancia de Enum de la clase <? extiende Enum> usando el valor de cadena?

Tengo dos tipos: Enum

enum Shape { 
    CAT, DOG; 
} 

enum Color { 
    BLUE, RED; 
} 

tengo un método:

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

me gustaría utilizar un método similar:

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

he tenido problemas para determinar exactamente cómo implementar getInstance(). Una vez que sepa la clase exacta Enum que desea crear una instancia, es fácil:

Color.valueOf("RED"); 

Pero ¿cómo puede esta línea anterior puede lograr con un desconocido Class? (Sin embargo, se sabe que el someEnumClass es una subclase de Enum).

¡Gracias!

Respuesta

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

y el método es para ser utilizado como:

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

Por otra parte, siempre se puede utilizar

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

que es un acceso directo para

Enum.valueOf(Shape.class, "CAT"); 
+0

+1 enumeraciones se construyen para que no necesita pasar por la maquinaria de reflexión tú mismo. Bien analizado y descubierto. –

+0

¿Por qué? Porque él lo pidió. Traté de señalar la solución a su pregunta. – chahuistle

+0

Excelente, gracias por la solución y una explicación detallada. El método Enum.valueOf() funcionó perfectamente. –

1

Nosotros quiere obtener el objeto Method que refleja el método valueOf del Class aprobado, que acepta un parámetro String; entonces invoke con ningún objeto (ya que es estática) y el parámetro de cadena de suministro:

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

Tendrá que coger un bote lleno de diferentes tipos de excepciones.

0

Puesto que usted tiene una idea de qué clase que usted está buscando simplemente puede pedir a la enumeración si sabe lo que le interesa:

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

Sin embargo, las cadenas de conectar en varias enumeraciones en busca de una el ajuste compromete el tipo de seguridad que obtienes con enumeraciones. Si asigna "rojo" a MyColor.RED y NuclearThreatWarningLevel.RED, entonces, como mínimo, puede terminar con la clase incorrecta. En el peor de los casos, podría terminar en su búnker subterráneo durante 6 meses esperando a que se despeje el aire, cuando todo lo que quería era un coche pintado de rojo :)

Sería mejor rediseñar esta área de su código si fuera posible para no tiene que convertir una cadena a una instancia de una de varias clases dinámicamente. Si expande su respuesta para incluir el problema que está tratando de resolver, tal vez la comunidad SO tenga algunas ideas.

+0

Ver mi @ en la respuesta de Chahuistle. No sé realmente qué clases de Enum existen en el modelo. En el futuro, algunos pueden ser agregados/eliminados. Uso la reflexión en los campos para determinar el tipo en tiempo de ejecución. Además, los valores se proporcionan como cadenas desde una carga de base de datos, por lo que debo ser flexible con respecto a las clases/valores que puedo manejar. –

+0

@craig, ¿el nombre de la clase está almacenado en el db como una cadena? ¿Cómo sabes qué clase instanciar? – Paul

+0

El nombre de clase no está almacenado en el DB. Tengo un par de clases "A", "B" y "C" que contienen un ivar a algún tipo de Enum. Los enumerados se escriben estáticamente en las clases, por ej. "A" tiene un Color Enum, y "B" tiene un Shape Enum, pero estoy asignando estos campos Enum usando un único método que usa la reflexión para determinar (en función de alguna instancia de A, B o C, y una cadena valor) que tipo de Enum necesita ser utilizado. –

1

Así que aquí está el código usando la validación de Spring y funciona muy bien para mí. El código completo se proporciona a continuación.

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

} 

Implementación de la clase anterior:

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

    } 

} 

USO DE ARRIBA La anotación es muy simple

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

¡Bienvenido a StackOverflow! OP está pidiendo obtener el valor de Enum por clase Enum y String. – kukido

Cuestiones relacionadas