30

I'm finding it difficult to put the exact question into words, so I'll just give an example.

I have two Enum types:

enum Shape {
    CAT, DOG;
}

enum Color {
    BLUE, RED;
}

I have a method:

public Object getInstance(String value, Class<?> type);

I would like to use the method like:

// someValue is probably "RED", and someEnumClass is probably Color.class
Color c = getInstance(someValue, someEnumClass);

I've been having trouble determining exactly how to implement getInstance(). Once you know the exact Enum class that you want to instantiate, it's easy:

Color.valueOf("RED");

But how can this above line be accomplished with an unknown Class? (It is, however, known that the someEnumClass is a subclass of Enum.)

Thanks!

Craig Otis
  • 31,257
  • 32
  • 136
  • 234

4 Answers4

55
 public static <T extends Enum<T>> T getInstance(final String value, final Class<T> enumClass) {
     return Enum.valueOf(enumClass, value);
 }

And the method is to be used as:

final Shape shape = getInstance("CAT", Shape.class);

Then again, you can always use

final Shape shape = Shape.valueOf("CAT");

which is a shortcut for

Enum.valueOf(Shape.class, "CAT");
chahuistle
  • 2,627
  • 22
  • 30
  • +1 Enums are built for you not to need to go through the reflection machinery yourself. Well parsed and spotted. – Karl Knechtel Jul 20 '11 at 22:01
  • Why? Because he asked for it. I tried to point out the solution to his question. – chahuistle Jul 20 '11 at 22:35
  • Excellent, thanks for both the solution and a detailed explanation. The Enum.valueOf() method worked perfectly! – Craig Otis Jul 21 '11 at 02:15
  • 2
    Also @Paul, I'm building up instances of classes that have Enums as some ivars, but the values of the enumerations are provided as Strings. (Stored in a DB, for example.) I use reflection to determine the Enum's actual type, then use Enum.valueOf() with the known class, and the String value, to set the field. Using reflection and Enum.valueOf() allows this method to work without having to know what actual Enum classes exist. – Craig Otis Jul 21 '11 at 02:18
  • 1
    This is a much more succinct version of my question: http://stackoverflow.com/questions/5262096/how-do-i-get-the-value-of-an-enum-if-i-dont-know-the-class-at-compile-time – Craig Otis Jul 21 '11 at 02:26
  • Does not work for enums having overridden methods. – Learner Jun 21 '23 at 20:06
4

We want to get the Method object which reflects the valueOf method of the passed-in Class, which accepts a String parameter; then invoke it with no object (since it's static) and the supplied String parameter:

type.getDeclaredMethod("valueOf", String.class).invoke(null, value);

You will need to catch a boatload of different types of exceptions.

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
3

So here is the code being using Spring validation and works great for me. Full code given below.

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import javax.validation.Constraint;
import javax.validation.Payload;
import javax.validation.ReportAsSingleViolation;
import javax.validation.constraints.NotNull;

@Documented
@Constraint(validatedBy = EnumValidatorImpl.class)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@NotNull(message = "Value cannot be null")
@ReportAsSingleViolation
public @interface EnumValidator {

  Class<? extends Enum<?>> enumClazz();

  String message() default "Value is not valid";

  Class<?>[] groups() default {};

  Class<? extends Payload>[] payload() default {};

}

Implementation of the above class:

import java.util.ArrayList;
import java.util.List;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

public class EnumValidatorImpl implements ConstraintValidator<EnumValidator, String> {

  List<String> valueList = null;

  @Override
  public boolean isValid(String value, ConstraintValidatorContext context) {
    if(!valueList.contains(value.toUpperCase())) {
      return false;
    }
    return true;
  }

  @Override
  public void initialize(EnumValidator constraintAnnotation) {
    valueList = new ArrayList<String>();
    Class<? extends Enum<?>> enumClass = constraintAnnotation.enumClazz();

    @SuppressWarnings("rawtypes")
    Enum[] enumValArr = enumClass.getEnumConstants();

    for(@SuppressWarnings("rawtypes")
    Enum enumVal : enumValArr) {
      valueList.add(enumVal.toString());
    }

  }

}

USAGE OF THE ABOVE ANNOTATION IS VERY SIMPLE

 @JsonProperty("lead_id")
  @EnumValidator( enumClazz=DefaultEnum.class,message="This error is coming from the enum class", groups = {Group1.class })
  private String leadId;
Rajeev Singla
  • 788
  • 6
  • 7
0

Since you have an idea of what class you're looking for you can just ask the enum if it knows what you're interested in:

public enum MyColor
{
  RED  ("red", Color.RED),
  BLUE ("blue", Color.BLUE),
  TAUPE ("brownish", new COLOR(80,64,77));

  private final String _name;
  private final Color _color;

  MyColor(String name, Color color)
  {
    _name = name;
    _color = color;
  }

  public static Color parseColor(String colorName)
  {
    for (MyColor mc : MyColor.values())
    {
      if (mc._name.equalsIgnoreCase(colorName))
        return mc._color;
    }
    return null;
  }
}

However, plugging strings into multiple enums looking for a fit compromises the type safety you get with enums. If you map "red" to both MyColor.RED and NuclearThreatWarningLevel.RED then you may, at the very least, end up with the wrong class. At the worst you could end up in your underground bunker for 6 months waiting for the air to clear, when all you wanted was a car painted red :)

It would be better to redesign this area of your code if possible so you don't have to convert a string to an instance of one of several classes dynamically. If you expand your answer to include the problem you're trying to solve perhaps the SO community will have some ideas.

Paul
  • 19,704
  • 14
  • 78
  • 96
  • See my @ in chahuistle's answer. I don't actually know what Enum classes exist in the model. In the future, some may be added/removed. I use reflection on the fields to determine type at runtime. Also the values are provided as Strings from a database load, so I need to be flexible about what classes/values I can handle. – Craig Otis Jul 21 '11 at 02:21
  • @craig, is the class name stored in the db as a string? How do you know which class to instantiate? – Paul Jul 21 '11 at 02:43
  • The class name is not stored in the DB. I have a couple classes "A", "B", and "C" that each contain an ivar to some type of Enum. The Enums are statically typed in the classes, eg. "A" has a Color Enum, and "B" has a Shape Enum, but I'm assigning these Enum fields using a single method that uses reflection to determine (based on some instance of A, B, or C, and a String value) which Enum type needs to be used. – Craig Otis Jul 21 '11 at 11:41
  • This way, when we add another class "D" that uses a new Enum type ("Food") that didn't exist at the time of writing, the method that loads the values from the database will still work just fine without any tweaks or maintenance. – Craig Otis Jul 21 '11 at 11:42