Converting Java enums to values and back – with Java 8!

Yeah, I know, I know – it seems just like yesterday when I talked about this topic. I focused mostly on conversion to values and back in the context of JPA 2.1 converters. This time we’ll focus on that part that helped us with “reverse resolution” – that is you have the value (for instance an int, but not ordinal number, of course!) that represents particular enum instance – and you want that enum instance. Mapping between these values and enum instances is a bijection, of course.

Our solution (here on GitHub) works fine, but there are two limitations:

  • ConvertedEnumResolver depends on the common interface ConvertedEnum our enums must implement, hence it is implicitly tied to the conversion framework.
  • If we need another representation (mapping) to different set of values we have to develop new resolver class.

Ok, the first one may not be a big deal. The second one is a real thing though. I realized this limitation before – and few weeks later, here we are, with enum that has not only DB representation (int) but also some String representation for different purposes. New resolver class was developed… But was it really necessary?

Everything in the resolver class is the same – but the instance method getting the value on the enum. I’m not going for any reflection, forget it. But hey, we recently switched to Java 8 and I’ve heard it has these method references! If we can pass it to the resolver constructor… is it possible?

Those who know Java 8 know that the answer is indeed positive. Our new resolver will look like this (I renamed it since the last time):

/**
 * Helps reverse resolving of enums from any value back to enum instance.
 * Resolver uses provided function that obtains value from enum instance.
 *
 * @param <T> type of an enum
 * @param <Y> type of a value
 */
public final class ReverseEnumResolver<T extends Enum, Y> {
    private final String classCanonicalName;
    private final Map<Y, T> valueMap = new HashMap<>();

    public ReverseEnumResolver(Class<T> enumClass, Function<T, Y> toValueFunction) {
        classCanonicalName = enumClass.getCanonicalName();
        for (T t : enumClass.getEnumConstants()) {
            valueMap.put(toValueFunction.apply(t), t);
        }
    }

    public T get(Y value) {
        T enumVal = valueMap.get(value);

        if (enumVal == null) {
            throw new IllegalArgumentException("No enum constant for '" + value + "' in " + classCanonicalName);
        }
        return enumVal;
    }
}

There is no conversion mentioned anymore. Just reverse resolving. So, our new enum does not have to be “Converted” anymore – unless you still need this interface for JPA conversion (which you may). Notice those method references:

/** Now we don't need to implement any interface for the sake of conversion/reverse resolving itself. */
public enum SomeEntityType {
    NORMAL(0, "norm"),
    SPECIAL(1, "spec");

    private final Integer dbValue;
    private final String strValue;

    private SomeEntityType(Integer dbValue, String strValue) {
        this.dbValue = dbValue;
        this.strValue = strValue;
    }

    public Integer getDbValue() {
        return dbValue;
    }

    public String getStrValue() {
        return strValue;
    }

    // static resolving for DB values
    public static final ReverseEnumResolver<SomeEntityType, Integer> dbValueResolver =
        new ReverseEnumResolver<>(SomeEntityType.class, SomeEntityType::getDbValue);

    public static SomeEntityType fromDbValue(Integer dbValue) {
        return dbValueResolver.get(dbValue);
    }

    // static resolving for String values
    public static final ReverseEnumResolver<SomeEntityType, String> strResolver =
        new ReverseEnumResolver<>(SomeEntityType.class, SomeEntityType::getStrValue);

    public static SomeEntityType fromStrValue(String value) {
        return strResolver.get(value);
    }
}

Plus we demonstrated that you can use two different values (dbValue, strValue). Of course it works:

System.out.println("SomeEntityType.fromStrValue(\"norm\") = " + SomeEntityType.fromStrValue("norm"));
System.out.println("SomeEntityType.fromDbValue(1) = " + SomeEntityType.fromDbValue(1));

This prints NORMAL and SPECIAL – as expected. Code for the classes can be found here, the whole demo is here and I’ll point you also directly to the README addition for this solution.

While Java libs get more and more bloated, these Java 8 syntax additions are definitely more than welcome. 🙂

Advertisements

About virgo47
Java Developer by profession in the first place. Gamer and amateur musician. And father too. Naive believer in brighter future. Step by step.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s