Java: instantiating an enum using reflection

JavaReflectionEnums

Java Problem Overview


Suppose you have a text file like:

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

That you wish to to update a corresponding object accordingly:

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

Where all are different kind of enums.

I would like to have a generic way to instantiate the enum values. That is, at runtime using reflection, and without knowing the enum types of the object in advance.

I would have imagined something like this:

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

The question is: what should there be instead? Is it even possible to instantiate an unknown enum given its String representation?

Java Solutions


Solution 1 - Java

field.set(this, Enum.valueOf((Class<Enum>) field.getType(), value));
  • getClass() after getType() should not be called - it returns the class of a Class instance
  • You can cast Class<Enum>, to avoid generic problems, because you already know that the Class is an enum

Solution 2 - Java

Alternative solution with no casting

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
}

Solution 3 - Java

You have an extra getClass call, and you have to cast (more specific cast per Bozho):

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

Solution 4 - Java

The accepted answer results in warnings because it uses the raw type Enum instead of Enum<T extends Enum<T>>.

To get around this you need to use a generic method like this:

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

Call it like this:

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

Solution 5 - Java

You may code your Enum similar tho this:

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

Then you may easily create Enum from String without reflection:

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

This solution is not originated from myself. I read it from somewhere else, but I can't recall the source.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestiondagneliesView Question on Stackoverflow
Solution 1 - JavaBozhoView Answer on Stackoverflow
Solution 2 - JavaRebzieView Answer on Stackoverflow
Solution 3 - JavaMatthew FlaschenView Answer on Stackoverflow
Solution 4 - JavaDev VercerView Answer on Stackoverflow
Solution 5 - JavaThiamTeckView Answer on Stackoverflow