如何在Java中检查给定的字符串是否为任何给定枚举类型的一部分?

16

我有两个不同的枚举值,我想要能够输出一个给定的字符串是否是枚举集合的一部分。这是我的代码:

public class Check {
    public enum Filter{SIZE, DATE, NAME};
    public enum Action{COPY, DELETE, REMOVE};

    public boolean isInEnum(String value, Enum e){
        // check if string value is a part of a given enum
        return false;
    }

    public void main(){
        String filter = "SIZE";
        String action = "DELETE";
                // check the strings
        isInEnum(filter, Filter);
        isInEnum(action, Action);
    }
}

eclipse说在最后两行 "Filter无法解析为变量",但除此之外,看起来函数 "isInEnum" 中的 Enum 参数是错误的。

这里似乎有些问题,请问有人可以帮忙吗?


你说得对 :) 枚举只是一个普通的类,你不能像那样提到它的名称。枚举成员是其枚举的实例,而不是整个枚举。 - Marko Topolnik
6个回答

21

最简单(通常也是最有效的)方法如下:

public <E extends Enum<E>> boolean isInEnum(String value, Class<E> enumClass) {
  for (E e : enumClass.getEnumConstants()) {
    if(e.name().equals(value)) { return true; }
  }
  return false;
}

然后您调用 isInEnum(filter, Filter.class)


1
这个做法起作用了,但语法太疯狂了...需要学习一些关于泛型的知识。 - Tom
1
@MarkoTopolnik,当您遍历元素并且可以使用e.valueOf(value)时,为什么要使用Reflection?此外,请注意,使用valueOf时,如果名称不存在,则必须捕获IllegalArgumentException,如果字符串为空,则必须捕获NullPointerException - Ozzy
1
我曾在另一个SO问题上就此展开了一场激烈的争论,结果证明使用for循环实现几乎总是比Enum.valueOf中必须使用try/catch更快,尤其是当你不确定字符串是否为枚举值之一时。如果您需要,我可以提供具体数据来证明这一点。 - Louis Wasserman
2
实际上,我相信valueOf为每个枚举类型保留了一个Map<String, Object>的全局缓存,这使得它是常数时间 - 但是异常非常昂贵,几乎在所有情况下都会占据主导地位。 - Louis Wasserman
你甚至可以更短地编写它(Java 8):public static > boolean isInEnum(String value, Class enumClass) { return Arrays.asList(enumClass.getEnumConstants()).stream().anyMatch(e -> e.name().equals(value)); } - SWiggels
显示剩余8条评论

11

如果你不介意使用Apache commons lang3库,你可以使用以下代码:

EnumUtils.isValidEnum(Filter.class, filter)

根据文档

该方法与Enum.valueOf(java.lang.Class, java.lang.String)不同之处在于,它对于无效的枚举名称不会抛出异常。


1
我会注意的...问题只是要添加一个微不足道的实用程序,我们需要添加一个新的依赖项...依赖项是昂贵的...就像因为你找到了一把车钥匙而去买一辆车一样。 - Stunner
1
@Stunner 好的,我不会建议仅为此添加依赖项。但在您已经使用该库的项目中,这是一个舒适的解决方案。 - CheshireCat

7
这是路易斯·沃瑟曼的答案的Java 8/streams版本。(您也可以直接将此方法放入Enum类中,并删除一个参数)。
public boolean isInEnum(String value, Class<E> enumClass) {
    return Arrays.stream(enumClass.getEnumConstants()).anyMatch(e -> e.name().equals(value));
  }

4

我没有足够的积分直接评论Simon Tower的回答,但这里是他提到的第二种方法,直接在枚举类本身中实现:

 public static boolean isInEnum(String value) {
     return Arrays.stream(EnumClassNameGoesHere.values()).anyMatch(e -> e.name().equals(value));
}

0

也可以按以下方式完成:

public boolean isInEnum(String value, Enum e){
    return Enums.getIfPresent(e.class, value).isPresent();
}

更多细节可以在这里找到。


0

避免使用循环进行验证。

我建议使用valueOf。这个方法是内置于枚举中的,可能会被考虑用于编译时优化。

这就像实现一个静态的Map<String,EnumType>来优化查找,这是你可以考虑的另一个方案。

缺点是你必须使用异常处理机制来捕获非枚举值。

例子

public enum DataType {
  //...
  static public boolean has(String value) {
    if (value== null) return false;
    try { 
        // In this implementation - I want to ignore the case
        // if you want otherwise ... remove .toUpperCase()
        return valueOf(value.toUpperCase()); 
    } catch (IllegalArgumentException x) { 
        // the uggly part ...
        return false;
    }
  }
}

请注意,使用上述类型的实现,调用代码看起来更加清晰。现在,您的主函数将类似于以下内容:
public void main(){
    String filter = "SIZE";
    String action = "DELETE";

    // ...

    if (Filter.has(filter) && Action.has(action)) {
      // Appropriate action
    }
}

另一个选项是使用静态映射。您可以采用这种方法来缓存基于其他属性的所有排序索引。在下面的示例中,我允许每个枚举值具有别名列表。在这种情况下,查找索引将不区分大小写,强制转换为大写。

public enum Command {
  DELETE("del","rm","remove"),
  COPY("cp"),
  DIR("ls");

  private static final Map<String,Command> ALIAS_MAP = new HashMap<String,Command>();
  static {
    for (Command type:Command.values()) {
      ALIAS_MAP.put(type.getKey().toUpper(),type);
      for (String alias:type.aliases) ALIAS_MAP.put(alias.toUpper(),type);
    }
  }

  static public boolean has(String value) {
    return ALIAS_MAP.containsKey(value.toUpper());
  }

  static public Command fromString(String value) {
    if (value == null) throw new NullPointerException("alias null");
    Command command = ALIAS_MAP.get(value);
    if (command == null) throw new IllegalArgumentException("Not an alias: "+value);
    return command;
  }

  private List<String> aliases;
  private Command(String... aliases) {
    this.aliases = Arrays.asList(aliases);
  }
}

请注意有关异常开销的评论帖子。当我第一次发布时,它们对我是隐藏的。 - YoYo
我正在查看另一个类似的主题。一位用户提到,在这种情况下,JIT编译器会优化异常开销。由于所有的仪器设备,只有在调试时才能注意到开销。 - YoYo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接