我有一个类名到其枚举类的映射,并且我有将"SomeEnum.FIRST"
之类的字符串解析为实际对象的方法。但是Enum.valueOf
不接受Class<? extends Enum<?>>
,而地图无法存储Class<T extends Enum<T>>
。
对于代码,地图看起来像这样:
private static final HashMap<String, Class<? extends Enum<?>>> enumsMap;
static {
enumsMap = new HashMap<>();
// These are two DIFFERENT enum classes!
registerEnum(SomeEnum.class);
registerEnum(AnotherEnum.class);
}
private static void registerEnum(Class<? extends Enum<?>> enumClass) {
enumsMap.put(enumClass.getSimpleName(), enumClass);
}
这是解析器(已删除的不必要的代码):
public <T extends Enum<T>> Object[] parse(List<String> strParameters) {
Object[] parameters = new Object[strParameters.size()];
for (int i = 0; i < parameters.length; i++) {
String strParameter = strParameters.get(i);
int delim = strParameter.lastIndexOf('.');
String className = strParameter.substring(0, delim - 1);
String enumName = strParameter.substring(delim + 1);
Class<T> enumClass = (Class<T>) enumsMap.get(className);
parameters[i] = Enum.valueOf(enumClass, enumName);
}
return parameters;
}
现在,如果我将其称为parse
,我的IDE(Android Studio)会告诉我“未经检查的方法'parse(List)'调用”,而afaik这是因为该泛型类型。如果我在parse
中将其删除,它将无法编译,但警告会消失。有没有解决的办法?
答案 0 :(得分:4)
如果您有以下枚举:
enum Foo {
A, B, C
}
enum Bar {
D, E, F
}
然后,您可以使用以下代码来实现您正在谈论的地图类型。
class MyEnums {
private final Map<String, Class<? extends Enum<?>>> map = new HashMap<>();
public void addEnum(Class<? extends Enum<?>> e) {
map.put(e.getSimpleName(), e);
}
private <T extends Enum<T>> T parseUnsafely(String name) {
final int split = name.lastIndexOf(".");
final String enumName = name.substring(0, split);
final String memberName = name.substring(split + 1);
@SuppressWarnings("unchecked")
Class<T> enumType = (Class<T>) map.get(enumName);
return Enum.valueOf(enumType, memberName);
}
public Object parse(String name) {
return parseUnsafely(name);
}
public Object[] parseAll(String... names) {
return Stream.of(names)
.map(this::parse)
.collect(toList())
.toArray();
}
}
这不会 not 绕过未经检查的演员表;它只是暂时向您隐藏。您可以看到在哪里SuppressWarnings
用来掩盖关于enumType
的警告。通常最好的做法是在尽可能有限的范围内应用警告抑制。在这种情况下,它是针对单个任务的。尽管通常这可能是一个危险信号,但在当前情况下,我们知道映射中唯一的值实际上是枚举类,因为它们必须由addEnum
添加。
然后,它可以用作:
MyEnums me = new MyEnums();
me.addEnum(Foo.class);
me.addEnum(Bar.class);
System.out.println(me.parse("Foo.A"));
System.out.println(me.parse("Bar.E"));
System.out.println(Arrays.toString(me.parseAll("Foo.B", "Bar.D", "Foo.C")));
打印:
A
E
[B, D, C]
您会注意到,我将parseUnsafely
和parse
分成了不同的方法。我们不想直接公开parseUnsafely
的原因是,它通过其返回类型保证了我们无法实际执行。如果它暴露了,那么我们可以编写
Bar bar = me.parseUnsafely("Foo.B");
可以编译,但是在运行时由于强制转换类异常而失败。
答案 1 :(得分:1)
没有通用的类型取决于相应键的Map值的安全方法。
但是,您可以自己存储枚举常量:
private static final Map<String, Map<String, ?>> enumsMap;
static {
enumsMap = new HashMap<>();
// These are two DIFFERENT enum classes!
registerEnum(SomeEnum.class);
registerEnum(AnotherEnum.class);
}
private static <T extends Enum<T>> void registerEnum(Class<T> enumClass) {
Map<String, ?> valuesByName =
EnumSet.allOf(enumClass).stream().collect(
Collectors.toMap(Enum::name, Function.identity()));
enumsMap.put(enumClass.getSimpleName(), valuesByName);
}
public Object[] parse(List<String> strParameters) {
Object[] parameters = new Object[strParameters.size()];
for (int i = 0; i < parameters.length; i++) {
String strParameter = strParameters.get(i);
int delim = strParameter.lastIndexOf('.');
String className = strParameter.substring(0, delim);
String enumName = strParameter.substring(delim + 1);
Map<String, ?> enumValues = enumsMap.get(className);
parameters[i] = enumValues.get(enumName);
if (parameters[i] == null) {
throw new IllegalArgumentException("Class " + className
+ " does not contain constant " + enumName);
}
}
return parameters;
}
我已更改的内容:
enumsMap
现在为Map<String, Map<String, ?>>
。每个值都是由常量名称作为键的枚举常量的映射。 ?
就足够了;记住常数值是枚举是没有好处的,因为parse
返回Object[]
。registerEnum
具有泛型类型,以确保其参数为有效的枚举类型。它不存储类参数,而是存储该枚举的常量。parse
不需要通用类型,因为它返回Object[]
。parse
不使用任何Enum方法,因此不再关心泛型类型的安全性。strParameter.substring(0, delim);
而不是delim - 1
。您希望整个子字符串不超过句点。