This is a more generalized solution. I use it anytime an enum is mapped to a code value. It is especially helpful when you have an enum represent a series of discrete values that map, say, to a database table. Note that the next few types that are defined only have to be written once and then can be used on any enum.
You first define an interface that your enum (or a POJO to be stored in a collection) will implement:
public interface Coded<T> {
T getCode();
}
The following leverages Google Guava, however, you could perform null checks rather than using their Optional class:
public final class CodedFinder {
private CodedFinder() {}
public static <V, T extends Enum<T> & Coded<V>> T find(final Class<T> target, final V code) {
final Optional<T> found = findInternal(Arrays.asList(target.getEnumConstants()), code);
if (! found.isPresent()) {
throw new IllegalArgumentException(code.toString() + " is invalid for " + target.getSimpleName());
}
return found.get();
}
// Additional find methods for arrays and iterables redacted for clarity.
private static <V, T extends Coded<V>> Optional<T> findInternal(final Iterable<T> values, final V code) {
return Iterables.tryFind(values, CodedPredicate.of(code));
}
}
The method above uses Class#getEnumConstants to retrieve all the values defined in the enum. The actual find method that is being called can be used not just for enums, but also for array and collections, etc.
We have to define a Predicate to leverage Guava's find method:
public final class CodedPredicate<V, T extends Coded<V>> implements com.google.common.base.Predicate<T> {
private final V value;
private CodedPredicate(final V value) {
this.value = value;
}
public static <V, T extends Coded<V>> CodedPredicate<V, T> of(final V value) {
return new CodedPredicate<V, T>(value);
}
public boolean apply(final T current) {
return value.equals(current.getCode());
}
}
That predicate is generic, so you can use Coded<Integer> or any other POJO that has a sensible equals() implementation.
It seems like a lot of code, but really there are only three types defined above, and these can be shared across any number of projects. Whenever you want to search on a value, it becomes trivial:
public final class GenderTest {
@Test(groups="unit")
public static void testValid() {
assert CodedFinder.find(Gender.class, "male") == Gender.m;
assert CodedFinder.find(Gender.class, "female") == Gender.f;
}
@Test(groups="unit", expectedExceptions=IllegalArgumentException.class)
public static void testInvalid() {
CodedFinder.find(Gender.class, "foo");
}
public enum Gender implements Coded<String> {
m("male"), f("female");
private final String value;
private Gender(final String option) {
value = option;
}
public String getCode()
{
return value;
}
}
}