Best practice to look up Java Enum

前端 未结 10 838
甜味超标
甜味超标 2021-02-01 13:17

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

相关标签:
10条回答
  • 2021-02-01 13:33

    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);
        }
    }
    
    0 讨论(0)
  • 2021-02-01 13:36

    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.....")))
    
    0 讨论(0)
  • 2021-02-01 13:36

    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.

    0 讨论(0)
  • 2021-02-01 13:41

    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.

    0 讨论(0)
  • 2021-02-01 13:41

    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;
        }
    }
    
    0 讨论(0)
  • 2021-02-01 13:45

    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"); 
    }
    }
    
    0 讨论(0)
提交回复
热议问题