Java 使用枚举前请检查有效的枚举值

Java 使用枚举前请检查有效的枚举值,java,enums,Java,Enums,我正在尝试查找枚举集,因为我知道经常会出现引发异常的不匹配:我希望在执行查找之前检查值是否存在以避免异常。我的枚举如下所示: public enum Fruit { APPLE("apple"), ORANGE("orange"); ; private final String fruitname; Fruit(String fruitname) { this.fruitname = fruitname; } public

我正在尝试查找枚举集,因为我知道经常会出现引发异常的不匹配:我希望在执行查找之前检查值是否存在以避免异常。我的枚举如下所示:

public enum Fruit {
    APPLE("apple"),
    ORANGE("orange");
    ;
    private final String fruitname;
    Fruit(String fruitname) {
        this.fruitname = fruitname;
    }
    public String fruitname() {return fruitname;}
}
  public enum Note {

    DO, RE, MI, FA, SOL, LA, SI;

    static {
      Enum.valueOf(Note.class, Note.DO.name());
    }

    public static Note valueOfOrDefault(String name, Note defaultValue) throws Exception {
      return Enums.valueOfOrDefault(Note.class, name, defaultValue);
    }

    public static <T extends Enum<T>> boolean hasValueFor(String name) throws Exception {
      return Enums.hasValueFor(Note.class, name);
    }

  }
public class Mapper {
    /**
     * This method returns all names of an enum
     * @param e
     * @return
     */
    public static String[] getEnumNames(Class<? extends Enum<?>> e) {
        return Arrays.stream(e.getEnumConstants()).map(Enum::name).toArray(String[]::new);
    }

    /**
     * This method returns all the enum classes from a class
     * @return
     */
    public static Class<?>[] getENumClasses(){
        Class<?>[] x = EnumProto.class.getClasses();
        return x;
    }

    /**
     *This utility performs following:
     *- will get all enum classes from EnumProto
     *- will get all names against all classes
     *- checks against all names of enum class and returns true if name matches else returns false
     * @param enumClass
     * @param value
     * @return
     */
    public static Boolean enumValidator(String enumClass, String value) {
        Boolean bool=false;
        EnumProto.l = getENumClasses();
        for (Class x : EnumProto.l) {
            if (x.getSimpleName().equals(enumClass)) {
                try {
                    String enumNames[] = getEnumNames(x);
                    if ( ArrayUtils.contains( enumNames, value ) ) {
                        bool=true;
                        return bool;
                    }
                } catch (ClassCastException e) {
                }
            }
        }
        return bool;
    }

    /**
     * Driver method for testing purpose
     * @param args
     */
    public static void main(String args[]){
        System.out.println(enumValidator(EnumProto.Channel.class.getSimpleName(),"CALL"));
    }
}
在尝试使用相关的枚举之前,我想检查一下,比如,“banana”是否是我的枚举值之一。我可以遍历允许的值,将字符串与

Fruit.values()[i].fruitname
但我希望能够做到(psedoo代码):

可能吗?我是否应该完全使用其他东西(数组?贴图?)


编辑:最后我同意了纳瓦曼的建议,但感谢大家的帮助。

当我这样做时,我通常会将它移植到我的enum类中

public enum Fruit {
        APPLE("apple"),
        ORANGE("orange");

    // Order of initialisation might need adjusting, I haven't tested it.
    private static final Map<String, Fruit> lookup = new HashMap<String, Fruit>();
    private final String fruitname;
    Fruit(String fruitname) {
        this.fruitname = fruitname;
        lookup.put(fruitname, Fruit);
    }
    public String fruitname() {return fruitname;}

    public static Fruit fromFruitname(String fruitname) {
        return lookup.get(fruitname);
    }
}
公共枚举结果{
苹果(“苹果”),
橙色(“橙色”);
//初始化顺序可能需要调整,我还没有测试过。
私有静态最终映射查找=新建HashMap();
私有最终字符串名称;
水果(字符串水果名称){
this.fruthName=fruthName;
查找。放置(水果名称、水果);
}
公共字符串FROUTNAME(){return FROUTNAME;}
公共静态FroothName(字符串FroothName){
返回lookup.get(水果名);
}
}
但是:

  • 对于小枚举,单步遍历列表可能更有效
顺便说一下:

  • 在这种情况下,我会使用约定并使用name(),因为它与自定义名称相同,只是大小写不同(很容易修复)
  • 当必须查找的内容与name()值完全不同时,此解决方案更有用

我同意你的愿望,不想制造任何例外。这对性能有好处(因为对于构建堆栈跟踪而言,一个异常值相当于一千条指令),并且当您说它通常是找不到的情况时(因此,它不是一个异常情况),这是合乎逻辑的


我认为您提到的
for循环
是正确的,前提是您只有几个枚举值。它可能会有最好的表现。但我知道你不想要它


您可以构建一个映射来查找枚举值,这样可以避免异常,同时返回适当的枚举

更新:Trejkaz已经发布了执行此操作的代码



还请注意,有时,当没有实例匹配时,某些枚举不是返回
null
作为返回类型,而是有一个专用的实例(例如,将其称为EMPTY或NOT_FOUND)。优点是,所有调用代码都不必处理null,并且不存在任何
NullPointerException
风险。如果需要,可以有一个布尔方法,表示
isFound()
(除该实例外,返回true)。而真正需要将这些值与其他值区分开来的代码仍然可以,而那些不在乎的代码只是在不知道这个特殊情况的情况下传递实例。

我将是这里的反向者。。。我认为你的第一个冲动(抛出异常)是正确的

如果您在业务逻辑而不是UI中进行检查,那么在该级别不会有任何反馈给用户。(如果您没有签入UI,我们还有其他问题)。因此,正确的处理方法是抛出异常

当然,这并不意味着您必须将异常气泡上升到UI级别,从而缩短其余逻辑。我通常做的是将枚举赋值放在自己的小try-catch中,并通过重新赋值或您设计的任何其他优雅的解决方案来处理异常


简言之。。。你第一个想到的就是钱。随它去吧。只需将异常处理更改一点。

我真的不知道内置解决方案。因此,您可能需要自己编写一个静态方法

public enum Fruit {
   ...
   static public boolean isMember(String aName) {
       Fruit[] aFruits = Fruit.values();
       for (Fruit aFruit : aFruits)
           if (aFruit.fruitname.equals(aName))
               return true;
       return false;
   }
   ...
}

也许你根本不应该使用枚举?如果您经常需要处理枚举中未定义的值,那么您可能应该使用HashMap之类的东西,然后可以使用containsKey(),找出某个特定的键是否存在。

只需提及另一种可能性,即始终返回一个结果,这样调用代码就不必担心异常或条件检查。如果找不到字符串,请返回例如Fruit.UNKNOWN

例如:

public enum Fruit {
   public Fruit getValueOf(String name) {
        for (Fruit fruit : Fruit.values()) {
           if (fruit.fruitname.equals(name))
               return fruit;
           }
        }
        return UNKNOWN;
   }
   ...
}

以下是使用EnumSet.allOf填充映射的方法:

public enum Fruit {

    APPLE("apple"), 
    ORANGE("orange");

    private static final Map<String, Fruit> nameToValueMap = new HashMap<String, Fruit>();

    static {
        for (Fruit value : EnumSet.allOf(Fruit.class)) {
            nameToValueMap.put(value.name(), value);
        }
    }

    private final String fruitname;

    Fruit(String fruitname) {
        this.fruitname = fruitname;
    }

    public String fruitname() {
        return fruitname;
    }

    public static Fruit forName(String name) {
        return nameToValueMap.get(name);
    }
}
公共枚举结果{
苹果(“苹果”),
橙色(“橙色”);
私有静态最终映射nameToValueMap=newhashmap();
静止的{
for(水果值:EnumSet.allOf(水果类)){
nameToValueMap.put(value.name(),value);
}
}
私有最终字符串名称;
水果(字符串水果名称){
this.fruthName=fruthName;
}
公共字符串名称(){
返回水果名;
}
公共静态名称(字符串名称){
返回nameToValueMap.get(name);
}
}

有一个apache commons lang EnumUtils.isValidEnum()。不幸的是,在后台,这是使用try/catch逻辑并返回布尔值,但至少您的代码看起来很干净:

if(EnumUtils.isValidEnum(Fruit.class, fruitname)) { ....

您需要使用最新的commons-lang3库,因为commons lang 2.x没有此功能

这是我的解决方案。我创建了一个集合,这样您就不必指定构造函数。这还有一个额外的好处,即所查找的值必须与枚举的大小写匹配

public enum Fruit{
    Apple, 
    Orange;

    private final static Set<String> values = new HashSet<String>(Fruit.values().length);

    static{
        for(Fruit f: Fruit.values())
            values.add(f.name());
    }

    public static boolean contains( String value ){
        return values.contains(value);
    }

}
公共枚举结果{
苹果,
橙色
私有最终静态集值=新哈希集(Fruit.values().length);
静止的{
for(水果f:Fruit.values())
添加(f.name());
}
公共静态布尔包含(字符串值){
返回值。包含(值);
}
}

在java8中,您可以这样做

 public static boolean isValidFruit(final String fruit) {
    return Arrays.stream(Fruit.values())
        .map(Fruit::name)
        .collect(Collectors.toSet())
        .contains(fruit);
}
在OracleJDK中(使用JDK10.0.1进行了尝试),类
class
具有
enumcont字段
  public enum Note {

    DO, RE, MI, FA, SOL, LA, SI;

    static {
      Enum.valueOf(Note.class, Note.DO.name());
    }

    public static Note valueOfOrDefault(String name, Note defaultValue) throws Exception {
      return Enums.valueOfOrDefault(Note.class, name, defaultValue);
    }

    public static <T extends Enum<T>> boolean hasValueFor(String name) throws Exception {
      return Enums.hasValueFor(Note.class, name);
    }

  }
public class EnumProto {

    public static Class<?>[] l;

    public static enum Severity {
        UNKNOWN_SEVERITY
    }

    public static  enum UserType {
        UNKNOWN_USER_TYPE,
        INTERNAL_EMPLOYEE ,
        EXTERNAL_PARTY
    }

    public static enum Channel {
        UNKNOWN_CHANNEL,
        CALL,
        EMAIL,
        WEBFORM,
        FAX
    }

//You can add more enum classes
}
public class Mapper {
    /**
     * This method returns all names of an enum
     * @param e
     * @return
     */
    public static String[] getEnumNames(Class<? extends Enum<?>> e) {
        return Arrays.stream(e.getEnumConstants()).map(Enum::name).toArray(String[]::new);
    }

    /**
     * This method returns all the enum classes from a class
     * @return
     */
    public static Class<?>[] getENumClasses(){
        Class<?>[] x = EnumProto.class.getClasses();
        return x;
    }

    /**
     *This utility performs following:
     *- will get all enum classes from EnumProto
     *- will get all names against all classes
     *- checks against all names of enum class and returns true if name matches else returns false
     * @param enumClass
     * @param value
     * @return
     */
    public static Boolean enumValidator(String enumClass, String value) {
        Boolean bool=false;
        EnumProto.l = getENumClasses();
        for (Class x : EnumProto.l) {
            if (x.getSimpleName().equals(enumClass)) {
                try {
                    String enumNames[] = getEnumNames(x);
                    if ( ArrayUtils.contains( enumNames, value ) ) {
                        bool=true;
                        return bool;
                    }
                } catch (ClassCastException e) {
                }
            }
        }
        return bool;
    }

    /**
     * Driver method for testing purpose
     * @param args
     */
    public static void main(String args[]){
        System.out.println(enumValidator(EnumProto.Channel.class.getSimpleName(),"CALL"));
    }
}