2010-10-25 25 views
26

J'essaie de contourner une erreur de compilation ("Bound mismatch: ...") relative à la recherche dynamique d'énumération.Enum.valueOf (Classe <T> enumType, String name) question

Fondamentalement, je veux réaliser quelque chose comme ceci:

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

Tout ce que je fais, je finis toujours avec cette erreur de compilation. Honnêtement, génériques et enums sont assez ahurissants pour moi ...

Qu'est-ce que je fais mal ici?

Répondre

22

Je pense que cela ne fonctionnera pas exactement comme ceci, sauf si vous avez accès à une variable de type (via une signature de type ou de méthode). Le problème est la signature de la méthode de Enum.valueOf:

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

Il n'y a pas moyen d'obtenir un T sans variable de type. Mais vous pouvez le faire comme si vous êtes prêt à supprimer certains avertissements du compilateur:

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

Sortie:

ELVIS

+0

La question est sur les énumérations, les génériques et la réflexion. Si vous ignorez les médicaments génériques En particulier pour les "types rares" comme 'Class '. –

+2

Le point est que a) il ne peut pas fonctionner autrement sans une méthode d'aide ou un type et b) nous savons avec certitude que tout 'Class 'satisfera aussi' Class > '(parce que c'est ainsi que fonctionnent les classes enum) même s'il n'y a aucun moyen de vérifier cela sans une variable de type. '@ SuppressWarnings' est une annotation que vous ne devriez utiliser que si vous savez ce que vous faites, et je sais. –

19

Le problème est avec Class<? extends Enum<?>>. Nous voulons E extends Enum<E>, mais nous ne pouvons pas obtenir cela parce que nous avons deux caractères génériques distincts.

Nous avons donc besoin d'introduire un paramètre générique possible introduit en appelant une méthode:

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

Mais la réflexion est vaseux et très rarement ce que vous voulez. Ne fais pas ça.

+1

Merci pour cette explication, cela m'a beaucoup aidé à mieux comprendre ce qui se passait. Puisque, dans mon cas d'utilisation particulier, je n'ai rien à fournir en tant que paramètre générique, je répondrai à la question de «seanizer». – Tom

6

Il existe en fait une autre approche: vous pouvez utiliser Class.getEnumConstants et lancer votre propre implémentation de Enum.valueOf qui n'a pas les mêmes problèmes de type. L'inconvénient est que vous obtenez un Enum<?> générique - mais si vous saviez de quel type vous disposiez, vous pouvez utiliser Enum.valueOf de toute façon.

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

(Notez que ma version retourne null s'il n'y a pas une telle constante plutôt que de jeter un IllegalArgumentException, mais c'est une chose triviale à changer.