webentwicklung-frage-antwort-db.com.de

Java: Instantiierung einer Aufzählung mit Reflektion

Angenommen, Sie haben eine Textdatei wie:

my_setting = ON
some_method = METHOD_A
verbosity = DEBUG
...

Dass Sie ein entsprechendes Objekt entsprechend aktualisieren möchten:

Setting my_setting = ON;
Method some_method = METHOD_A;
Verbosity verbosity = DEBUG;
...

Wo alle verschiedene Arten von Enums sind.

Ich hätte gerne einen generischen Weg, die Enumerationswerte zu instanziieren. Das heißt, zur Laufzeit mit Reflektion und ohne die Aufzählungstypen des Objekts im Voraus zu kennen.

Ich hätte mir so etwas vorstellen können:

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 //...
}

Die Frage ist: Was sollte es stattdessen geben? Ist es sogar möglich, eine unbekannte Enumenzuordnung aufgrund ihrer String-Darstellung zu instanziieren?

42
dagnelies
field.set(this, Enum.valueOf((Class<Enum>) field.getType(), value));
  • getClass() nach getType() sollte nicht aufgerufen werden - es gibt die Klasse einer Class-Instanz zurück
  • Sie können Class<Enum> umsetzen, um generische Probleme zu vermeiden, da Sie bereits wissen, dass die Class eine enum ist.
90
Bozho

Alternative Lösung ohne Guss

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

Sie haben einen zusätzlichen getClass-Aufruf, und Sie müssen Besetzung durchführen (spezifischer Besetzung pro Bozho)

field.set(test, Enum.valueOf((Class<Enum>) field.getType(), value));
4

Sie können Ihr Enum ähnlich wie folgt codieren:

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

Dann können Sie Enum ohne Rücksicht auf String erstellen:

Setting my_settings = Setting.fromString("ON");

Diese Lösung stammt nicht von mir. Ich habe es von anderswo gelesen, aber ich kann mich nicht an die Quelle erinnern.

0
ThiamTeck

Die akzeptierte Antwort führt zu Warnungen, da sie den Rohtyp Enum anstelle von Enum <T verwendet, um Enum <T >> zu erweitern.

Um dies zu umgehen, müssen Sie eine generische Methode wie diese verwenden:

@SuppressWarnings("unchecked")
private <T extends Enum<T>> T createEnumInstance(String name, Type type) {
  return Enum.valueOf((Class<T>) type, name);
}

Nenne es so:

Enum<?> enum = createEnumInstance(name, field.getType());
field.set(this, enum);
0
Dev Vercer