查找 Java Enum 的最佳实践
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2418729/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Best practice to look up Java Enum
提问by Marcus Leon
We have a REST API where clients can supply parameters representing values defined on the server in Java Enums.
我们有一个 REST API,客户端可以在其中提供表示在 Java Enums 中定义在服务器上的值的参数。
So we can provide a descriptive error, we add this lookup
method to each Enum. Seems like we're just copying code (bad). Is there a better practice?
所以我们可以提供一个描述性错误,我们将这个lookup
方法添加到每个 Enum 中。似乎我们只是在复制代码(不好)。有没有更好的做法?
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);
}
}
}
Update: The default error message provided by valueOf(..)
would be No enum const class a.b.c.MyEnum.BadValue
. I would like to provide a more descriptive error from the API.
更新:提供的默认错误消息valueOf(..)
是No enum const class a.b.c.MyEnum.BadValue
. 我想从 API 中提供更具描述性的错误。
采纳答案by Mykola Golubyev
Probably you can implement generic static lookup
method.
也许您可以实现通用静态lookup
方法。
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.
或显式调用实用程序类查找方法。
回答by Kannan Ekanath
Why do we have to write that 5 line code ?
为什么我们要写那 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");
}
}
回答by Adam
If you want the lookup to be case insensitive you can loop through the values making it a little more friendly:
如果您希望查找不区分大小写,您可以遍历值使其更友好:
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);
}
}
回答by Vincent Robert
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.
抓住一个IllegalArgumentException
以RuntimeException
更清晰的消息重新抛出另一个可能看起来是个好主意,但事实并非如此。因为这意味着您关心异常中的消息。
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;
}
}
回答by Robin
The error message in IllegalArgumentException is already descriptive enough.
IllegalArgumentException 中的错误消息已经具有足够的描述性。
Your method makes a generic exception out of a specific one with the same message simply reworded. A developer would prefer the specific exception type and can handle the case appropriately instead of trying to handle RuntimeException.
您的方法从特定的异常中创建了一个通用异常,并简单地改写了相同的消息。开发人员更喜欢特定的异常类型,并且可以适当地处理这种情况,而不是尝试处理 RuntimeException。
If the intent is to make the message more user friendly, then references to values of enums is irrelevant to them anyway. Let the UI code determine what should be displayed to the user, and the UI developer would be better off with the IllegalArgumentException.
如果目的是使消息对用户更友好,那么对枚举值的引用无论如何都与它们无关。让 UI 代码决定应该向用户显示什么,UI 开发人员最好使用 IllegalArgumentException。
回答by makrom
update: As GreenTurtle correctly remarked, the following is wrong
更新:正如 GreenTurtle 正确评论的那样,以下是错误的
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.
这可能比捕获运行时异常性能低,但可以使代码更清晰。捕捉这样的异常总是一个坏主意,因为它很容易被误诊。当检索比较值本身导致 IllegalArgumentException 时会发生什么?这将被视为枚举器的非匹配值。
回答by Radu Sebastian LAZIN
We do all our enums like this when it comes to Rest/Json etc. It has the advantage that the error is human readable and also gives you the accepted value list. We are using a custom method MyEnum.fromString instead of MyEnum.valueOf, hope it helps.
当涉及到 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;
}
}
so for example if you call
所以例如如果你打电话
MyEnum value = MyEnum.fromString("X");
you'll get an IllegalArgumentException with the following message:
您将收到带有以下消息的 IllegalArgumentException:
'X' has no corresponding value. Accepted values: [A, B, C, D]
'X' 没有对应的值。接受值:[A、B、C、D]
you can change the IllegalArgumentException to a custom one.
您可以将 IllegalArgumentException 更改为自定义异常。
回答by Ken Chan
Guava also provides such function which will return an Optional
if an enum cannot be found.
Guava 还提供了这样的函数,Optional
如果找不到枚举,它将返回一个。
Enums.getIfPresent(MyEnum.class, id).toJavaUtil()
.orElseThrow(()-> new RuntimeException("Invalid enum blah blah blah.....")))
回答by mskfisher
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);
}
}
回答by Crozeta
Apache Commons Lang 3 contais the class EnumUtils. If you aren't using Apache Commons in your projects, you're doing it wrong. You are reinventing the wheel!
Apache Commons Lang 3 包含类 EnumUtils。如果您没有在项目中使用 Apache Commons,那么您就做错了。你在重新发明轮子!
There's a dozen of cool methods that we could use without throws an Exception. For example:
我们可以使用十几种很酷的方法而不会抛出异常。例如:
Gets the enum for the class, returning null if not found.
This method differs from Enum.valueOf in that it does not throw an exceptionfor an invalid enum name and performs case insensitive matching of the name.
获取类的枚举,如果未找到则返回 null。
此方法与 Enum.valueOf 的不同之处在于它不会为无效的枚举名称引发异常,并且执行名称的大小写不敏感匹配。
EnumUtils.getEnumIgnoreCase(SeasonEnum.class, season);