我们有一个REST API,客户端可以在Java Enums中提供表示服务器上定义的值的参数。
因此我们可以提供描述性错误,我们将此lookup
方法添加到每个枚举。好像我们只是在复制代码(坏)。有更好的做法吗?
public enum MyEnum {
A, B, C, D;
public static MyEnum lookup(String id) {
try {
return MyEnum.valueOf(id);
} catch (IllegalArgumentException e) {
throw new RuntimeException("Invalid value for my enum blah blah: " + id);
}
}
}
更新:valueOf(..)
提供的默认错误消息为No enum const class a.b.c.MyEnum.BadValue
。我想从API中提供更具描述性的错误。
答案 0 :(得分:33)
您可以实现通用的静态lookup
方法。
喜欢这样
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;
}
}
然后你可以
public enum MyEnum {
static public MyEnum lookup(String id) {
return LookupUtil.lookup(MyEnum.class, id);
}
}
或显式调用实用程序类查找方法。
答案 1 :(得分:13)
看起来你在这里做的不好,但不在你想的地方。
用更清晰的信息抓取IllegalArgumentException
重新抛出另一个RuntimeException
可能看起来不错,但事实并非如此。因为这意味着您关心异常中的消息。
如果您关心异常中的消息,那么这意味着您的用户以某种方式看到了异常。这很糟糕。
如果要向用户提供显式错误消息,则应在解析用户输入时检查枚举值的有效性,并在用户输入不正确时在响应中发送相应的错误消息。
类似的东西:
// 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;
}
}
答案 2 :(得分:3)
为什么我们要写那5行代码?
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");
}
}
答案 3 :(得分:3)
如果您希望查找不区分大小写,则可以遍历值,使其更友好:
public enum MyEnum {
A, B, C, D;
public static MyEnum lookup(String id) {
boolean found = false;
for(MyEnum enum: values()){
if(enum.toString().equalsIgnoreCase(id)) found = true;
}
if(!found) throw new RuntimeException("Invalid value for my enum: " +id);
}
}
答案 4 :(得分:2)
IllegalArgumentException中的错误消息已经足够描述了。
您的方法使用相同的消息重新编写特定的异常。开发人员更喜欢特定的异常类型,并且可以适当地处理案例,而不是尝试处理RuntimeException。
如果目的是使消息更加用户友好,那么对枚举值的引用无论如何都与它们无关。让UI代码确定应该向用户显示什么,并且UI开发人员最好使用IllegalArgumentException。
答案 5 :(得分:2)
更新:正如GreenTurtle正确评论,以下是错误的
我会写
boolean result = Arrays.asList(FooEnum.values()).contains("Foo");
这可能不如捕获运行时异常高,但可以使代码更清晰。抓住这些例外总是一个坏主意,因为它容易被误诊。 检索比较值本身会导致IllegalArgumentException时会发生什么?然后,这将被视为枚举器的非匹配值。
答案 6 :(得分:2)
当涉及到Rest / Json等时,我们会做这样的所有枚举。 它的优点是错误是人类可读的,并且还为您提供了可接受的值列表。我们使用自定义方法MyEnum.fromString而不是MyEnum.valueOf,希望它有所帮助。
public enum MyEnum {
A, B, C, D;
private static final Map<String, MyEnum> NAME_MAP = Stream.of(values())
.collect(Collectors.toMap(MyEnum::toString, Function.identity()));
public static MyEnum fromString(final String name) {
MyEnum myEnum = NAME_MAP.get(name);
if (null == myEnum) {
throw new IllegalArgumentException(String.format("'%s' has no corresponding value. Accepted values: %s", name, Arrays.asList(values())));
}
return myEnum;
}
}
所以例如,如果你打电话
MyEnum value = MyEnum.fromString("X");
您将收到IllegalArgumentException,并显示以下消息:
'X'没有相应的值。可接受的值:[A,B,C,D]
您可以将IllegalArgumentException更改为自定义。
答案 7 :(得分:1)
番石榴还提供了这样的功能,如果找不到枚举,它将返回Variable 'lon' used before being initialized
Variable 'lat' used before being initialized
。
Optional
答案 8 :(得分:0)
您可以使用静态查找映射来避免异常并返回null,然后根据需要抛出:
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);
}
}
答案 9 :(得分:0)
Apache Commons Lang 3包含EnumUtils类。如果您在项目中未使用Apache Commons,那么您做错了。您正在重新发明轮子!
我们可以使用许多很酷的方法而不会引发异常。例如:
获取该类的枚举,如果找不到,则返回null。
此方法与Enum.valueOf的不同之处在于它不会抛出 无效的枚举名称,并且不区分大小写 名称的匹配。
EnumUtils.getEnumIgnoreCase(SeasonEnum.class, season);