Supposons que vous ayez un fichier texte du type:
my_setting = ON
some_method = METHOD_A
verbosity = DEBUG
...
Que vous souhaitiez mettre à jour un objet correspondant en conséquence:
Setting my_setting = ON;
Method some_method = METHOD_A;
Verbosity verbosity = DEBUG;
...
Où tous sont différents types d'énums.
Je voudrais avoir un moyen générique pour instancier les valeurs enum. C'est-à-dire que, lors de l'exécution, on utilise la réflexion et sans connaître à l'avance les types d'enum de l'objet.
J'aurais imaginé quelque chose comme ça:
for (ConfigLine line : lines)
{
String[] tokens = line.string.split("=", 2);
String name = tokens[0].trim();
String value = tokens[1].trim();
try
{
Field field = this.getClass().getDeclaredField(name);
if(field.getType().isEnum())
{
// doesn't work (cannot convert String to enum)
field.set(this, value);
// invalid code (some strange generics issue)
field.set(this, Enum.valueOf(field.getType().getClass(), value));
}
else
{ /*...*/ }
}
catch //...
}
La question est: que devrait-il y avoir à la place? Est-il même possible d'instancier une énumération inconnue étant donné sa représentation sous forme de chaîne?
field.set(this, Enum.valueOf((Class<Enum>) field.getType(), value));
getClass()
après getType()
ne devrait pas être appelé - il retourne la classe d'une instance Class
Class<Enum>
pour éviter les problèmes génériques, car vous savez déjà que Class
est une enum
Solution alternative sans coulée
try {
Method valueOf = field.getType().getMethod("valueOf", String.class);
Object value = valueOf.invoke(null, param);
field.set(test, value);
} catch ( ReflectiveOperationException e) {
// handle error here
}
Vous avez un appel getClass
supplémentaire et vous devez lancer (distribution plus spécifique par Bozho):
field.set(test, Enum.valueOf((Class<Enum>) field.getType(), value));
Vous pouvez coder votre Enum similaire à ceci:
public enum Setting {
ON("ON"),OFF("OFF");
private final String setting;
private static final Map<String, Setting> stringToEnum = new ConcurrentHashMap<String, Setting>();
static {
for (Setting set: values()){
stringToEnum.put(set.setting, set);
}
}
private Setting(String setting) {
this.setting = setting;
}
public String toString(){
return this.setting;
}
public static RequestStatus fromString(String setting){
return stringToEnum.get(setting);
}
}
Ensuite, vous pouvez facilement créer Enum à partir de String sans réflexion:
Setting my_settings = Setting.fromString("ON");
Cette solution ne vient pas de moi. Je l'ai lu ailleurs, mais je ne me souviens pas de la source.
La réponse acceptée entraîne des avertissements car elle utilise le type brut Enum au lieu de Enum <T étend Enum <T >>.
Pour résoudre ce problème, vous devez utiliser une méthode générique comme celle-ci:
@SuppressWarnings("unchecked")
private <T extends Enum<T>> T createEnumInstance(String name, Type type) {
return Enum.valueOf((Class<T>) type, name);
}
Appelez ça comme ça:
Enum<?> enum = createEnumInstance(name, field.getType());
field.set(this, enum);