我正在尝试查找枚举集,知道经常会有一个不匹配的异常抛出:我想在执行查找之前检查该值是否存在以避免异常。我的枚举看起来像这样:
public enum Fruit {
APPLE("apple"),
ORANGE("orange");
;
private final String fruitname;
Fruit(String fruitname) {
this.fruitname = fruitname;
}
public String fruitname() {return fruitname;}
}
我想在尝试使用相关的枚举之前检查“香蕉”是否是我的枚举值之一。我可以遍历允许的值,将我的字符串与
进行比较Fruit.values()[i].fruitname
但我希望能够做一些像(pseduo-code)这样的事情:
if (Fruit.values().contains(myStringHere)) {...
这可能吗?我应该完全使用其他东西吗(数组?地图?)?
编辑:最后我和NawaMan的建议一致,但感谢所有人提供了有用的建议。
答案 0 :(得分:25)
我真的不知道内置的解决方案。因此,您可能必须自己编写静态方法。
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;
}
...
}
答案 1 :(得分:21)
有一个apache commons lang EnumUtils.isValidEnum()。不幸的是,在幕后,这是使用try / catch逻辑并返回布尔值,但至少你的代码看起来很干净:
if(EnumUtils.isValidEnum(Fruit.class, fruitname)) { ....
你需要使用最新的commons-lang3库作为commons-lang 2.x没有这个功能。
答案 2 :(得分:8)
当我这样做时,我通常把它移植到我的枚举课上。
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);
}
}
可是:
顺便提及:
答案 3 :(得分:7)
这是使用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);
}
}
答案 4 :(得分:6)
这是我的解决方案。我创建了一个集合,因此您不必指定构造函数。这也有额外的好处,即查找的值必须与枚举的情况相匹配。
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);
}
}
答案 5 :(得分:5)
我会成为这里的逆势者...我认为你的第一个冲动(抛出异常)是正确的做法。
如果您在业务逻辑而不是UI中进行检查,则该级别不会向用户提供任何反馈。 (如果您没有在UI中检查,我们还有其他问题)。因此,处理它的正确方法是抛出异常。
当然,这并不意味着您必须将异常气泡升级到UI级别,从而使其余逻辑短路。我通常做的就是将枚举赋值放在它自己的小try-catch中,并通过重新分配或者你设计的任何其他优雅解决方案来处理异常。
简而言之......你第一次想到的就是赚钱了。去吧。只需将您的异常处理更改一点即可。
答案 6 :(得分:3)
在java8中你可以这样做
public static boolean isValidFruit(final String fruit) {
return Arrays.stream(Fruit.values())
.map(Fruit::name)
.collect(Collectors.toSet())
.contains(fruit);
}
答案 7 :(得分:2)
我同意你没有创造任何例外的愿望。它对性能有好处(作为一个例外值得一千个指令,用于构建堆栈跟踪),并且当你说通常是找不到它时(因此,它不是例外<)是合乎逻辑的/ em>条件)。
如果您只有少量枚举值,我认为您提到的for loop
是正确的。它可能会拥有最好的性能。但我知道你不想要它。
您可以构建一个Map来查找枚举值,这样可以避免异常并同时返回相应的枚举。
更新:Trejkaz已经发布了执行此操作的代码。
另请注意,有时候,当没有实例匹配时,不是返回null
作为返回类型,而是某些枚举有一个专用实例(例如,将其称为EMPTY或NOT_FOUND)。优点是所有调用代码都不必处理空值,并且没有NullPointerException
的风险。如果需要,可以使用一个布尔方法isFound()
(除了该实例外返回true)。并且真正需要将这些值与其他值区分开来的代码仍然可以,而那些不关心的代码只是在不知道这种特殊情况的情况下传递实例。
答案 8 :(得分:2)
也许你根本不应该使用Enum?如果你经常不得不处理你的枚举中没有定义的值,也许你应该使用像HashMap&lt; String,Fruit&gt;这样的东西。然后,您可以使用containsKey()来查明是否存在特定键。
答案 9 :(得分:2)
只是提到另一种可能性,即让你的调用代码不必担心异常或条件检查就是总是返回一个Fruit。如果找不到该字符串,则返回Fruit.UNKNOWN,例如。
示例:
public enum Fruit {
public Fruit getValueOf(String name) {
for (Fruit fruit : Fruit.values()) {
if (fruit.fruitname.equals(name))
return fruit;
}
}
return UNKNOWN;
}
...
}
答案 10 :(得分:1)
您也可以这样: 将所有枚举都放在一个类中,例如:
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"));
}
}
通过这种通用方法,您可以检查传递的字符串是否为枚举之一。
答案 11 :(得分:0)
在Oracle JDK(尝试使用JDK 10.0.1)中,类Class
具有字段enumConstantDirectory
。该字段的类型为Map<String, T>
的{{1}}。它按名称存储枚举Class<T>
的常量。枚举类已初始化后,T
仍为空。在第一次调用enumConstantDirectory
时,给定枚举Enum.valueOf(Class<T> enumType, String name)
的所有常量都存储在T
中。
由于每个枚举类都有自己的映射,因此我们可以尝试使用它,而不是为一个/某个/每个枚举创建一个附加的本地映射。
我首先实现了一个实用程序类:
enumConstantDirectory
可以这样使用:
public class Enums {
private static final Field DIRECTORY_FIELD;
static {
try {
DIRECTORY_FIELD = Class.class.getDeclaredField("enumConstantDirectory");
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
public static <T extends Enum<T>> T valueOfOrDefault(Class<T> enumType, String name, T defaultValue) throws Exception {
return getEnumConstantDirectory(enumType).getOrDefault(name, defaultValue);
}
public static <T extends Enum<T>> boolean hasValueFor(Class<T> enumType, String name) throws Exception {
Map<String, T> enumConstantDirectory = getEnumConstantDirectory(enumType);
return enumConstantDirectory.containsKey(name);
}
private static <T extends Enum<T>> Map<String, T> getEnumConstantDirectory(Class<T> enumType) throws Exception {
try {
DIRECTORY_FIELD.setAccessible(true);
Map<String, T> enumConstantDirectory = (Map<String, T>) DIRECTORY_FIELD.get(enumType);
return enumConstantDirectory;
}
finally {
DIRECTORY_FIELD.setAccessible(false);
}
}
}
总结:
通常可以检查名称是否代表枚举常量,而无需其他映射或遍历枚举常量。但是像往常一样,存在反射的缺点。此外,还需要确保将枚举的常量存储在其类中。