查找Java Enum的最佳实践

Mar*_*eon 43 java enums

我们有一个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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

更新:提供的默认错误消息valueOf(..)No enum const class a.b.c.MyEnum.BadValue.我想从API中提供更具描述性的错误.

Myk*_*yev 34

可能你可以实现通用的静态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;
   }
}
Run Code Online (Sandbox Code Playgroud)

然后你可以

public enum MyEnum {
   static public MyEnum lookup(String id) {
       return LookupUtil.lookup(MyEnum.class, id);
   }
}
Run Code Online (Sandbox Code Playgroud)

或者显式调用实用程序类查找方法.

  • 仍然不明白你为什么要这样做.你在实用程序类抛出的运行时异常中包装`IllegalArgumentException` ...获得了什么收益?Enum中`valueOf()`实现抛出的默认异常就足够了:它给出了无效类型,即输入作为异常消息的一部分. (12认同)
  • 而不是调用`Enum.valueOf(MyEnum.class,id)`而不是调用`MyEnum.valueOf(id)`.由于`IllegalArgumentException`比`RuntimeException`更合适,根本不需要该实用方法. (5认同)

Vin*_*ert 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 我建议你不应该捕获通用异常以返回它们的消息,而是返回消息以解释为什么异常被捕获.根据您的异常进行通用错误响应是内部体系结构泄露给客户端,这是一种不好的做法.API的错误消息不应该依赖于实现的错误报告体系结构(在本例中为Java异常). (5认同)

Rad*_*ZIN 8

当涉及到 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

所以例如如果你打电话

MyEnum value = MyEnum.fromString("X");
Run Code Online (Sandbox Code Playgroud)

您将收到带有以下消息的 IllegalArgumentException:

'X' 没有对应的值。接受值:[A、B、C、D]

您可以将 IllegalArgumentException 更改为自定义异常。


Ken*_*han 7

Guava 还提供了这样的函数,Optional如果找不到枚举,它将返回一个。

Enums.getIfPresent(MyEnum.class, id).toJavaUtil()
            .orElseThrow(()-> new RuntimeException("Invalid enum blah blah blah.....")))
Run Code Online (Sandbox Code Playgroud)


Wil*_*eta 6

Apache Commons Lang 3 包含 EnumUtils 类。如果您没有在项目中使用 Apache Commons,那么您就做错了。你正在重新发明轮子!

我们可以使用许多很酷的方法而不会引发异常。例如:

获取类的枚举,如果未找到则返回 null。

此方法与 Enum.valueOf 的不同之处在于,它不会针对无效的枚举名称引发异常,并执行名称的不区分大小写的匹配。

EnumUtils.getEnumIgnoreCase(SeasonEnum.class, season);
Run Code Online (Sandbox Code Playgroud)


Ada*_*dam 5

如果您希望查找不区分大小写,您可以循环遍历这些值,使其更加友好:

 public enum MyEnum {
   A, B, C, D;

      public static MyEnum lookup(final String id) {
        for(MyEnum enumValue: values()){
           if(enumValue.name().equalsIgnoreCase(id)){
              return enumValue;
           }
        }  
        throw new RuntimeException("Invalid value for my enum: " + id);
       }
}
Run Code Online (Sandbox Code Playgroud)