【问题标题】:How can I get the proper enum that implements a particular interface in Java?如何获得在 Java 中实现特定接口的正确枚举?
【发布时间】:2018-12-29 11:41:04
【问题描述】:

我有如下界面:

public interface Moon {
    // Enums can not extend any class in Java,
    // thus I am implementing an interface
    double getMass();
    double getDiameter();
}

由我的枚举实现(我省略了构造函数和方法实现)

public enum Saturn implements Moon {
    ATLAS(30.2, 6.6),
    PAN(28.2, 4.95);

    private double Diameter;
    private double Mass;

    // constructor and methods implementation
}

以及其他行星的类似枚举:

public enum Mars implements Moon {
    PHOBOS(22.2, 1.08),
    DEIMOS(12.6, 2);

    private double Diameter;
    private double Mass;

    // constructor and methods implementation
}

问题

有两个字符串:

String planetName = "Mars";
String moonName = "PHOBOS";

我怎样才能以聪明的方式获得特定的枚举?

在只有一个枚举类的情况下,它是valueOf 方法的简单使用,但我如何检查所有实现特定接口的枚举?

Moon something = <??planetName??>.valueOf(moonName);

【问题讨论】:

  • 太棒了!正是我的用例!

标签: java enums


【解决方案1】:

您可以使用行星名称到相关枚举类型的映射,并使用它来执行查找。

例如,使用地图(肯定有替代方法):

Map<String, Class<? extends Enum>> planetMoonTypes = new HashMap<>();

planetMoonTypes.put("Saturn", Saturn.class);
planetMoonTypes.put("Mars", Mars.class);

使用这样的地图,您可以使用以下方法查找 "PHOBOS" 值:

Moon phobos = (Moon) Enum.valueOf(planetMoonTypes.get("Mars"), "PHOBOS");

上面会返回Mars.PHOBOS


编辑:关于添加非月球枚举的能力。您可以封装地图并强制设置适当的界限。例如,下面使用了一个方法:

class MoonRegistry {

    private final Map<String, Class<? extends Enum>> 
              planetMoonTypes = new HashMap<>();

    {
        this.registerEnum("Saturn", Saturn.class);
        this.registerEnum("Mars", Mars.class);
    }

    public <T extends Enum<T> & Moon> void registerEnum(
            String planetName, Class<T> cls) {
        this.planetMoonTypes.put(planetName, cls);
    }

    public Moon findByPlanetAndName(String planet, String name) {
        return (Moon) Enum.valueOf(this.planetMoonTypes.get(planet), name);
    }
}

同样,这个解决方案不能是完全类型安全的,因为有多个枚举子类型(如您所见,原始 Enum 类型在实例字段的类型中)。但是如果这被正确封装并且你的代码在这个类之外没有使用原始类型,你可以确保只添加实现Moon 的枚举类型。

【讨论】:

  • 这看起来很干净。但是,正如我所看到的,此映射确保映射中的类是枚举(extends enum),但它不限于实现Moon 接口的类。因此,我可以在没有警告的情况下向该地图添加潜在的任何枚举。
  • @matandked 没错。您可以使用通用方法扩展解决方案以使其具有适当的界限。我会编辑。
  • @matandked 检查编辑(当然还有最后一句话:-))
  • @Boann 谢谢 :-) 我会改正的 - 我不知道我是怎么做到的 :D
【解决方案2】:

如果你真的希望它是动态的,如果我不推荐它,你可以使用反射事件来实现。你也可以

【讨论】:

  • 老实说,通过反射获取所有实现类的想法几乎是可怕的。凳子,不错的答案!
【解决方案3】:

如果你所有的枚举都在同一个包中,那么这个解决方案可以工作。

String planetName = "Mars";
String moonName = "PHOBOS";

try {
    Moon moons[] = (Moon[]) Class.forName("your_package_name" + planetName).getEnumConstants();

    // The following line is not really needed, but I've added it as an 
    // illustration as to what you could do with the moons[]
    Moon planet = Arrays.stream(moons).filter(i -> i.toString().equals(moonName))
                          .findFirst().orElse(null);

} catch (ClassNotFoundException e) {
    e.printStackTrace();
}

【讨论】:

  • 我们是否需要将所有内容都转换为Object / 不能使用Moon istead?
  • 是的,我们可以。我已经编辑了答案来说明这一点。
【解决方案4】:

我怀疑您的设计存在弱点,导致您需要寻找复杂的方法来获取所需的枚举实例。当每个行星都是独特的卫星时,为每个行星设置一个单独的枚举可能没有多大意义。最好重新考虑一下设计。比如:

interface Body {
    double getMass();
    double getDiameter();
}

enum Planet implements Body {
    MERCURY, VENUS...
}

enum Moon implements Body {
    ATLAS, PHOBOS, DEIMOS...

    public Planet getPlanet();
}

这样很容易通过名称获得行星或卫星。或者通过以下方式获取行星的所有卫星:

Arrays.stream(Moon.values())
    .filter(m -> m.getPlanet().equals(this))
    ...

【讨论】:

  • 这是个好建议。
【解决方案5】:

考虑使用额外的enum Plantes,并将其卫星分配为像这样的枚举属性

public enum Planets {
    MARS(Mars.values()), SATURN(Saturn.values());

    private final Enum<?>[] Moons;

    private Planets(final Enum<?>[] Moons) {
        this.Moons = Moons;
    }

    public Moon getMoon(final String moon) {
        return (Moon) Arrays
                .stream(this.Moons)
                .filter(e -> e.name().equals(moon))
                .findFirst()
                .get();
    }
}

它从已经定义的枚举中获取卫星并将其分配给每个行星的枚举。还有一个getMoon()(函数valueOf()已被枚举本身使用),它搜索月亮。

你可以这样使用它:

    // Outputs 22.2
    System.out.println("" + Planets.valueOf("MARS").getMoon("PHOBOS").getDiameter());

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-11-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-18
    • 1970-01-01
    • 2016-04-12
    • 1970-01-01
    相关资源
    最近更新 更多