We have a REST API where clients can supply parameters representing values defined on the server in Java Enums.
So we can provide a descriptive error, we add this
You can use a static lookup map to avoid the exception and return a null, then throw as you'd like:
public enum Mammal {
COW,
MOUSE,
OPOSSUM;
private static Map<String, Mammal> lookup =
Arrays.stream(values())
.collect(Collectors.toMap(Enum::name, Function.identity()));
public static Mammal getByName(String name) {
return lookup.get(name);
}
}
Guava also provides such function which will return an Optional
if an enum cannot be found.
Enums.getIfPresent(MyEnum.class, id).toJavaUtil()
.orElseThrow(()-> new RuntimeException("Invalid enum blah blah blah.....")))
update: As GreenTurtle correctly remarked, the following is wrong
I would just write
boolean result = Arrays.asList(FooEnum.values()).contains("Foo");
This is possibly less performant than catching a runtime exception, but makes for much cleaner code. Catching such exceptions is always a bad idea, since it is prone to misdiagnosis. What happens when the retrieval of the compared value itself causes an IllegalArgumentException ? This would then be treaten like a non matching value for the enumerator.
Probably you can implement generic static lookup
method.
Like so
public class LookupUtil {
public static <E extends Enum<E>> E lookup(Class<E> e, String id) {
try {
E result = Enum.valueOf(e, id);
} catch (IllegalArgumentException e) {
// log error or something here
throw new RuntimeException(
"Invalid value for enum " + e.getSimpleName() + ": " + id);
}
return result;
}
}
Then you can
public enum MyEnum {
static public MyEnum lookup(String id) {
return LookupUtil.lookup(MyEnum.class, id);
}
}
or call explicitly utility class lookup method.
Looks like you have a bad practice here but not where you think.
Catching an IllegalArgumentException
to rethrow another RuntimeException
with a clearer message might look like a good idea but it is not. Because it means you care about messages in your exceptions.
If you care about messages in your exceptions, then it means that your user is somehow seeing your exceptions. This is bad.
If you want to provide an explicit error message to your user, you should check the validity of the enum value when parsing user input and send the appropriate error message in the response if user input is incorrect.
Something like:
// This code uses pure fantasy, you are warned!
class MyApi
{
// Return the 24-hour from a 12-hour and AM/PM
void getHour24(Request request, Response response)
{
// validate user input
int nTime12 = 1;
try
{
nTime12 = Integer.parseInt(request.getParam("hour12"));
if( nTime12 <= 0 || nTime12 > 12 )
{
throw new NumberFormatException();
}
}
catch( NumberFormatException e )
{
response.setCode(400); // Bad request
response.setContent("time12 must be an integer between 1 and 12");
return;
}
AMPM pm = null;
try
{
pm = AMPM.lookup(request.getParam("pm"));
}
catch( IllegalArgumentException e )
{
response.setCode(400); // Bad request
response.setContent("pm must be one of " + AMPM.values());
return;
}
response.setCode(200);
switch( pm )
{
case AM:
response.setContent(nTime12);
break;
case PM:
response.setContent(nTime12 + 12);
break;
}
return;
}
}
Why do we have to write that 5 line code ?
public class EnumTest {
public enum MyEnum {
A, B, C, D;
}
@Test
public void test() throws Exception {
MyEnum.valueOf("A"); //gives you A
//this throws ILlegalargument without having to do any lookup
MyEnum.valueOf("RADD");
}
}