2010-10-25 15 views
26

Estoy tratando de evitar un error de compilación ("Bound mismatch: ...") relacionado con la búsqueda de enum dinámica.Enum.valueOf (Clase <T> enumType, String name) pregunta

Básicamente quiero lograr algo como esto:

String enumName = whatever.getEnumName(); 
Class<? extends Enum<?>> enumClass = whatever.getEnumClass(); 
Enum<?> enumValue = Enum.valueOf(enumClass, enumName); 

lo que haga, siempre termino con ese error de compilación. Honestamente, los genéricos y las enumeraciones son bastante alucinantes para mí ...

¿Qué estoy haciendo mal aquí?

Respuesta

22

Creo que no funcionará exactamente así a menos que tenga acceso a una variable de tipo (mediante un tipo o firma de método). El problema es la firma del método de Enum.valueOf:

public static <T extends Enum<T>> T valueOf(
    Class<T> enumType, 
    String name 
); 

No hay manera de conseguir una T sin una variable de tipo. Pero usted puede hacerlo de esta manera, si usted está dispuesto a suprimir algunas advertencias del compilador:

public enum King{ 
    ELVIS 
} 

@SuppressWarnings({ "unchecked", "rawtypes" }) 
public static void main(final String[] args){ 
    final Class<? extends Enum> enumType = King.class; 
    final Enum<?> theOneAndOnly = Enum.valueOf(enumType, "ELVIS"); 
    System.out.println(theOneAndOnly.name()); 
} 

Salida:

ELVIS

+0

La pregunta se refiere a las enumeraciones, genéricos y la reflexión. Si ignora los genéricos ¿Para qué sirven los "tipos raros" como "Class ". –

+2

El punto es que a) no puede funcionar de otra forma sin un método o tipo de ayuda yb) sabemos con certeza que cualquier 'Clase 'también satisfará' Class > '(porque esa es la manera en que funcionan las clases enum) aunque no hay forma de verificar eso sin una variable de tipo. '@ SuppressWarnings' es una anotación que solo debería usar si sabe lo que está haciendo, y lo sé. –

19

El problema es con Class<? extends Enum<?>>. Queremos E extends Enum<E>, pero no podemos obtener eso porque tenemos dos comodines distintos.

así que tenemos que introducir un parámetro genérico, es posible introducido llamando a un método:

enum MyEnum { 
    ME; 
} 

public class EnName { 
    public static void main(String[] args) { 
     Enum<?> value = of(MyEnum.class, "ME"); 
     System.err.println(value); 
    } 
    private static <E extends Enum<E>> E of(Class<E> clazz, String name) { 
     E value = Enum.valueOf(clazz, name); 
     return value; 
    } 
} 

Pero la reflexión es sucio y muy rara vez lo que desea. No lo hagas

+1

Gracias por esta explicación, ayudó mucho a comprender qué está pasando. Como en mi caso de uso particular no tengo nada que proporcionar como parámetro genérico, buscaré la respuesta de seanizer. – Tom

6

En realidad, existe un enfoque alternativo: puede usar Class.getEnumConstants y ejecutar su propia implementación de Enum.valueOf que no tenga los mismos problemas de tipo. La desventaja es que obtienes un Enum<?> genérico, pero si supieses de qué tipo venías entrando, podrías usar Enum.valueOf de todos modos.

private static Enum<?> findEnumValue(Class<? extends Enum<?>> enumType, String value) { 
    return Arrays.stream(enumType.getEnumConstants()) 
       .filter(e -> e.name().equals(value)) 
       .findFirst() 
       .orElse(null); 
} 

(Tenga en cuenta que mi versión vuelve null si no hay tal constante en lugar de lanzar una IllegalArgumentException, pero eso es una cosa trivial para cambiar.

Cuestiones relacionadas