如何从第一个匹配的枚举类型

时间:2016-11-10 22:21:54

标签: java generics enums

注意:这个问题更多的是关于泛型而不是关于枚举。

我有很少的枚举类型,都实现了一个通用接口IEffect

例如

enum ElementalEffect implements IEffect {
    FIRE, WATER;
}
enum CombatEffect implements IEffect {
    PARALYSIS, SLEEP;
}

我想解析一个配置文件,它应该为武器添加效果。为此,我必须将一个给定的名称解析为其中一个效果。为了保持简单,我想写一个像这样的方法(主要是伪代码,这不会编译。实际上问题的关键是如何进行编译):

IEffect resolveEffectName(String name, Class... clazzes) {
    for(Class clazz : clazzes) {
        try {
            return Enum.valueOf(clazz, name);
        } catch(IllegalArgumentException) { /* ignore, try next class */}
    }
    throw new IllegalArgumentException("No matching effect found for " + name);
}
// resolveEffectName(readNameFromFile, ElementalEffect.class, CombatEffect.class);

现在我遇到的问题是,如果没有编译器告诉我

,我无法弄清楚如何编写该方法
  

Enum类型中的方法valueOf(Class<T>, String)不适用于参数...

人们说它应该是

private static ICombatEffectType getFirstResolved(String name, Class<? extends Enum<?>>... classes) {
    for (Class<? extends Enum<?>> clazz : classes) {
        try {
            return Enum.valueOf(clazz, name);
        } catch (IllegalArgumentException e) {
        }
    }
    return null;
}

这不起作用。随意尝试(如果你不相信我)。

  

Enum类型中的方法valueOf(Class<T>, String)不适用于参数(Class<capture#6-of ? extends Enum<?>>, String)

4 个答案:

答案 0 :(得分:2)

如果这是你的风格,你可以流利地写这个:

class FluentGetter {
  private final String name;
  private IEffect found;

  FluentGetter(String name) { this.name = name; }

  <T extends Enum<T> & IEffect> FluentGetter search(Class<T> clazz) {
    if (found == null) { // If you've already found something, don't overwrite that.
      try {
        found = Enum.valueOf(clazz, name);
      } catch (IllegalArgumentException e) {}
    }
    return this;
  }

  IEffect get() {
    return found;  // + check if it's null, if you want.
  }
}

然后:

IEffect effect =
    new FluentGetter(name)
        .search(ElementalEffect.class)
        .search(CombatEffect.class)
        .get();

通过为每个类分别进行方法调用,避免了类数组的泛型边界问题。

很确定我不会自己使用它;只是把它作为一种选择扔掉。

答案 1 :(得分:1)

你真正想要的是枚举常量的名称映射,你可以使用流轻松制作:

{{1}}

答案 2 :(得分:1)

我找到了一种方法,可以使用包装类使所有内容无需警告:

private static class Wrapper<T extends Enum<T> & IEffect> {
    private Class<T> clazz;

    public Wrapper(Class<T> clazz) {
        this.clazz = clazz;
    }

    public IEffect resolveName(String name) {
        return Enum.valueOf(clazz, name);
    }
}
private static IEffect getFirstResolved(String name, Wrapper<?>... clazzes) {
    for (Wrapper<?> clazz : clazzes) {
        try {
            return clazz.resolveName(name);
        } catch (IllegalArgumentException e) {}
    }
    throw new IllegalArgumentException("No enum has a member " + name);
}

// Example call
ICombatEffectType elemental = getFirstResolved(
            type,
            new Wrapper<>(ElementalType.class),
            new Wrapper<>(StatusEffect.class));

答案 3 :(得分:0)

这应该使用您的第一种方法编译(带警告):

private static final Map<String, IEffect> constants
    = Stream.of(ElementalEffect.values(), CombatEffect.values())
        .flatMap(Arrays::stream)
        .collect(Collectors.toMap(Enum::name, Function.identity()));

IEffect resolveEffectName(String name) {
    if(!constants.containsKey(name))
        throw new IllegalArgumentException("No matching effect found for " + name);

    return constants.get(name);
}