2010-09-17 30 views
36

Supponiamo di avere un file di testo come:Java: istanziare un enum utilizzando la riflessione

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

che si desidera aggiornare un corrispondente oggetto di conseguenza:

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

dove tutti sono diversi tipi di enumerazioni .

Mi piacerebbe avere un modo generico per istanziare i valori enum. Cioè, in fase di esecuzione utilizzando la riflessione, e senza conoscere in anticipo i tipi di enumerazione dell'oggetto.

avrei immaginato qualcosa di simile:

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 domanda è: che cosa dovrebbe esserci, invece? È persino possibile creare un'istanza sconosciuta data la sua rappresentazione in stringa?

risposta

80
field.set(this, Enum.valueOf((Class<Enum>) field.getType(), value)); 
  • getClass() dopo getType() non dovrebbe essere chiamato - restituisce la classe di un Class istanza
  • È possibile trasmettere Class<Enum>, per evitare problemi generici, perché si sa già che lo Class è un enum
+0

Phunky! (Più 8 per andare) –

+0

questo si traduce in un errore di farmaci generici compilatore: Il metodo valueOf (Classe , String) nel tipo Enum non è applicabile per gli argomenti (classe , Object) – onejigtwojig

+0

Che compilatore .. – Bozho

4

Hai un extra getClass chiamata, e si deve lanciare (fusione di più specifico per Bozho):

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

il problema si verifica ancora generici – Bozho

0

Si possono codificare l'Enum simile tho questo:

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

allora si può creare facilmente Enum da String senza riflessione:

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

Questa soluzione non è originato da me stesso. L'ho letto da qualche altra parte, ma non riesco a ricordare la fonte.

+0

Non molto utile se si utilizza il reflection, perché non si sa se l'enum in questione stia usando questa impostazione. – Kolky

2

soluzione alternativa senza fusione

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

I risultati risposta accettata in avvertimenti perché utilizza il tipo grezzo Enum invece di Enum < T estende Enum <T> >.

Per ovviare a questo è necessario utilizzare un metodo generico come questo:

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

chiamare in questo modo:

Enum<?> enum = createEnumInstance(name, field.getType()); 
field.set(this, enum); 
Problemi correlati